From 66a2aa01d995a0e3d60d7d7df48ba77b98a6817b Mon Sep 17 00:00:00 2001 From: Vinayak Menon Date: Fri, 11 Mar 2016 18:24:49 +0530 Subject: [PATCH 001/296] arm/arm64: dma-mapping: flush the tlb on unremap Make sure there are no stale tlb entries when dma_unremap returns, thus preventing speculative fetches. Change-Id: I22070de282f25fe5ea20177e67a6d629123e29a4 Signed-off-by: Vinayak Menon Signed-off-by: Ramesh Gupta Guntha Signed-off-by: anupritaisno1 --- arch/arm/mm/dma-mapping.c | 2 ++ arch/arm64/mm/dma-mapping.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index d2465a107f889..0b9d771ddbe57 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -160,6 +160,8 @@ static void arm_dma_unremap(struct device *dev, void *remapped_addr, return; } vunmap(remapped_addr); + flush_tlb_kernel_range((unsigned long)remapped_addr, + (unsigned long)(remapped_addr + size)); } struct dma_map_ops arm_dma_ops = { diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 2678f6e17db5c..01d9e5c503469 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -506,6 +506,8 @@ static void arm64_dma_unremap(struct device *dev, void *remapped_addr, return; } vunmap(remapped_addr); + flush_tlb_kernel_range((unsigned long)remapped_addr, + (unsigned long)(remapped_addr + size)); } const struct dma_map_ops noncoherent_swiotlb_dma_ops = { From 76a6b88773862c8b60fb54b0f8a8f0b40d21786b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Mar 2014 21:49:33 +0100 Subject: [PATCH 002/296] cpufreq: Skip current frequency initialization for ->setpolicy drivers After commit da60ce9f2fac (cpufreq: call cpufreq_driver->get() after calling ->init()) __cpufreq_add_dev() sometimes fails for CPUs handled by intel_pstate, because that driver may return 0 from its ->get() callback if it has not run long enough to collect enough samples on the given CPU. That didn't happen before commit da60ce9f2fac which added policy->cur initialization to __cpufreq_add_dev() to help reduce code duplication in other cpufreq drivers. However, the code added by commit da60ce9f2fac need not be executed for cpufreq drivers having the ->setpolicy callback defined, because the subsequent invocation of cpufreq_set_policy() will use that callback to initialize the policy anyway and it doesn't need policy->cur to be initialized upfront. The analogous code in cpufreq_update_policy() is also unnecessary for cpufreq drivers having ->setpolicy set and may be skipped for them as well. Since intel_pstate provides ->setpolicy, skipping the upfront policy->cur initialization for cpufreq drivers with that callback set will cover intel_pstate and the problem it's been having after commit da60ce9f2fac will be addressed. Fixes: da60ce9f2fac (cpufreq: call cpufreq_driver->get() after calling ->init()) Change-Id: Ie51ddf53138e489fd057044fc8c11bea5a6ea915 References: https://bugzilla.kernel.org/show_bug.cgi?id=71931 Reported-and-tested-by: Patrik Lundquist Acked-by: Dirk Brandewie Cc: 3.13+ # 3.13+ Signed-off-by: Rafael J. Wysocki Signed-off-by: anupritaisno1 --- drivers/cpufreq/cpufreq.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index cdff0721fe1a5..e3de5edecccc4 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2201,8 +2201,13 @@ int cpufreq_update_policy(unsigned int cpu) * BIOS might change freq behind our back * -> ask driver for current freq and notify governors about a change */ - if (cpufreq_driver->get) { + if (cpufreq_driver->get && !cpufreq_driver->setpolicy) { new_policy.cur = cpufreq_driver->get(cpu); + if (WARN_ON(!new_policy.cur)) { + ret = -EIO; + goto no_policy; + } + if (!policy->cur) { pr_debug("Driver did not initialize current freq"); policy->cur = new_policy.cur; From a8b39ac09f612d71e784217ee0392323744acbd1 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Wed, 18 Jun 2014 11:27:32 -0700 Subject: [PATCH 003/296] cpufreq: unlock when failing cpufreq_update_policy() Commit bd0fa9bb455d introduced a failure path to cpufreq_update_policy() if cpufreq_driver->get(cpu) returns NULL. However, it jumps to the 'no_policy' label, which exits without unlocking any of the locks the function acquired earlier. This causes later calls into cpufreq to hang. Fix this by creating a new 'unlock' label and jumping to that instead. Fixes: bd0fa9bb455d ("cpufreq: Return error if ->get() failed in cpufreq_update_policy()") Change-Id: I5c9f8e4776b3d4027eca68c8c514c296c1798882 Link: https://devtalk.nvidia.com/default/topic/751903/kernel-3-15-and-nv-drivers-337-340-failed-to-initialize-the-nvidia-kernel-module-gtx-550-ti-/ Signed-off-by: Aaron Plattner Cc: 3.15+ # 3.15+ Signed-off-by: Rafael J. Wysocki Signed-off-by: anupritaisno1 --- drivers/cpufreq/cpufreq.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e3de5edecccc4..cb9f136d730a1 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2183,10 +2183,8 @@ int cpufreq_update_policy(unsigned int cpu) struct cpufreq_policy new_policy; int ret; - if (!policy) { - ret = -ENODEV; - goto no_policy; - } + if (!policy) + return -ENODEV; down_write(&policy->rwsem); @@ -2205,7 +2203,7 @@ int cpufreq_update_policy(unsigned int cpu) new_policy.cur = cpufreq_driver->get(cpu); if (WARN_ON(!new_policy.cur)) { ret = -EIO; - goto no_policy; + goto unlock; } if (!policy->cur) { @@ -2220,10 +2218,10 @@ int cpufreq_update_policy(unsigned int cpu) ret = cpufreq_set_policy(policy, &new_policy); +unlock: up_write(&policy->rwsem); cpufreq_cpu_put(policy); -no_policy: return ret; } EXPORT_SYMBOL(cpufreq_update_policy); From 86dbacf50d0077c8cc6478ab1c8475789c34c4c8 Mon Sep 17 00:00:00 2001 From: Junjie Wu Date: Mon, 13 Apr 2015 17:30:13 -0700 Subject: [PATCH 004/296] qcom-cpufreq: Rename cpufreq_suspend to suspend_data cpufreq_suspend is now a function in core CPUfreq framework. Rename qcom-cpufreq's local per-cpu variable to suspend_data. Change-Id: I2f567f0c04271d728d4e6a17b61cea2152c4d8f7 Signed-off-by: Junjie Wu Signed-off-by: anupritaisno1 --- drivers/cpufreq/qcom-cpufreq.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c index 00c7bcbffd7f3..8231c159cb086 100644 --- a/drivers/cpufreq/qcom-cpufreq.c +++ b/drivers/cpufreq/qcom-cpufreq.c @@ -42,7 +42,7 @@ struct cpufreq_suspend_t { int device_suspended; }; -static DEFINE_PER_CPU(struct cpufreq_suspend_t, cpufreq_suspend); +static DEFINE_PER_CPU(struct cpufreq_suspend_t, suspend_data); static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq, unsigned int index) @@ -78,12 +78,12 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, int index; struct cpufreq_frequency_table *table; - mutex_lock(&per_cpu(cpufreq_suspend, policy->cpu).suspend_mutex); + mutex_lock(&per_cpu(suspend_data, policy->cpu).suspend_mutex); if (target_freq == policy->cur) goto done; - if (per_cpu(cpufreq_suspend, policy->cpu).device_suspended) { + if (per_cpu(suspend_data, policy->cpu).device_suspended) { pr_debug("cpufreq: cpu%d scheduling frequency change " "in suspend.\n", policy->cpu); ret = -EFAULT; @@ -111,7 +111,7 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, ret = set_cpu_freq(policy, table[index].frequency, table[index].driver_data); done: - mutex_unlock(&per_cpu(cpufreq_suspend, policy->cpu).suspend_mutex); + mutex_unlock(&per_cpu(suspend_data, policy->cpu).suspend_mutex); return ret; } @@ -241,9 +241,9 @@ static int msm_cpufreq_suspend(void) int cpu; for_each_possible_cpu(cpu) { - mutex_lock(&per_cpu(cpufreq_suspend, cpu).suspend_mutex); - per_cpu(cpufreq_suspend, cpu).device_suspended = 1; - mutex_unlock(&per_cpu(cpufreq_suspend, cpu).suspend_mutex); + mutex_lock(&per_cpu(suspend_data, cpu).suspend_mutex); + per_cpu(suspend_data, cpu).device_suspended = 1; + mutex_unlock(&per_cpu(suspend_data, cpu).suspend_mutex); } return NOTIFY_DONE; @@ -255,7 +255,7 @@ static int msm_cpufreq_resume(void) struct cpufreq_policy policy; for_each_possible_cpu(cpu) { - per_cpu(cpufreq_suspend, cpu).device_suspended = 0; + per_cpu(suspend_data, cpu).device_suspended = 0; } /* @@ -476,8 +476,8 @@ static int __init msm_cpufreq_register(void) int cpu, rc; for_each_possible_cpu(cpu) { - mutex_init(&(per_cpu(cpufreq_suspend, cpu).suspend_mutex)); - per_cpu(cpufreq_suspend, cpu).device_suspended = 0; + mutex_init(&(per_cpu(suspend_data, cpu).suspend_mutex)); + per_cpu(suspend_data, cpu).device_suspended = 0; } rc = platform_driver_probe(&msm_cpufreq_plat_driver, @@ -486,7 +486,7 @@ static int __init msm_cpufreq_register(void) /* Unblock hotplug if msm-cpufreq probe fails */ unregister_hotcpu_notifier(&msm_cpufreq_cpu_notifier); for_each_possible_cpu(cpu) - mutex_destroy(&(per_cpu(cpufreq_suspend, cpu). + mutex_destroy(&(per_cpu(suspend_data, cpu). suspend_mutex)); return rc; } From fc78135ffe32db60e8d0a6ec18d7e6197d55c01f Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 15 Dec 2015 15:41:37 -0800 Subject: [PATCH 005/296] BACKPORT: 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 (cherry picked from commit 142afbc6b2f33832f332ce5b561aa817edfff0b4) Change-Id: Ibaaffde651e76be2defeaa081ae56ca9e8f93602 Signed-off-by: anupritaisno1 --- 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 9516dad451091..b3c6501ae3d2b 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -103,6 +103,7 @@ int netlink_sendskb(struct sock *sk, struct sk_buff *skb); 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); @@ -125,6 +126,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 8e0b6c856a130..d0c5bb84a257c 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -105,6 +105,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 @@ -116,6 +117,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 d943b361569e3..aef16d085faaa 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2567,6 +2567,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; @@ -2579,6 +2580,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 c7408dd8fd9a8..44feb83e0f24e 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -544,6 +544,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) { struct genl_ops *ops = cb->data; @@ -605,6 +619,7 @@ static int genl_family_rcv_msg(struct genl_family *family, struct netlink_dump_control c = { .module = family->module, .data = ops, + .start = genl_lock_start, .dump = genl_lock_dumpit, .done = genl_lock_done, }; @@ -616,6 +631,7 @@ static int genl_family_rcv_msg(struct genl_family *family, } else { struct netlink_dump_control c = { .module = family->module, + .start = genl_lock_start, .dump = ops->dumpit, .done = ops->done, }; From d6c7ee8d8db3448b90c7f43978167c8943ef68cf Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 19 Oct 2017 20:51:10 +0800 Subject: [PATCH 006/296] ipsec: Fix aborted xfrm policy dump crash 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. Change-Id: Id41cdd41c4e43e0c3ac30c5d03c15b8046d70845 Fixes: 12a169e7d8f4 ("ipsec: Put dumpers on the dump list") Signed-off-by: Herbert Xu Signed-off-by: Steffen Klassert Signed-off-by: anupritaisno1 --- 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 ff723b8122f51..0a2ff970b0ae0 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1564,31 +1564,33 @@ 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; xfrm_policy_walk_done(walk); 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; @@ -2352,6 +2354,7 @@ static const struct nla_policy xfrma_policy[XFRMA_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 *); } xfrm_dispatch[XFRM_NR_MSGTYPES] = { @@ -2363,6 +2366,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 }, @@ -2406,6 +2410,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, }; From c688d2cdf0245bb69d32d7d0d4941099af1e15cb Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 11 Aug 2017 02:11:33 +0900 Subject: [PATCH 007/296] 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 Signed-off-by: anupritaisno1 --- net/xfrm/xfrm_user.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 0a2ff970b0ae0..0ab8d7fef0a99 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -863,6 +863,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; } From 54238f3975b2dcc71e58f599ee321d23d5610a6e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 9 Dec 2017 19:24:58 +0000 Subject: [PATCH 008/296] ipsec: Fix aborted xfrm policy dump crash commit 1137b5e2529a8f5ca8ee709288ecba3e68044df2 upstream. This is a fix for CVE-2017-16939 suitable for older stable branches. The upstream fix is commit 1137b5e2529a8f5ca8ee709288ecba3e68044df2, from which the following explanation is taken: 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. It was not possible to define a 'start' callback for netlink dumps until Linux 4.5, so instead add a check for the initialisation flag in the 'done' callback. Change-Id: Id8a129889b11800178c37374ff9fee5af68ccff9 Signed-off-by: Ben Hutchings Signed-off-by: anupritaisno1 --- net/xfrm/xfrm_user.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 0ab8d7fef0a99..0ba7cf4093146 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1571,7 +1571,8 @@ static int xfrm_dump_policy_done(struct netlink_callback *cb) { struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args; - xfrm_policy_walk_done(walk); + if (cb->args[0]) + xfrm_policy_walk_done(walk); return 0; } From 0568bc22bba120557b2726a5f54421c9c80ab800 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 29 Jan 2018 21:31:21 -0800 Subject: [PATCH 009/296] 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 Signed-off-by: anupritaisno1 --- 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 36ae048edd77a..ccca427679deb 100755 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -828,8 +828,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct 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); @@ -845,12 +845,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; } @@ -876,8 +876,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; From a818dcfc735dc4db5d5572cbc62d32d116db54b5 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 17:51:26 -0800 Subject: [PATCH 010/296] 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 Signed-off-by: anupritaisno1 --- fs/sdcardfs/file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 1665200f3156e..c560360f25c07 100755 --- 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 @@ -261,7 +260,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); } From daa3d01cbba0714d5344eb6ff47f33e4c7719d0f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 15:02:50 -0800 Subject: [PATCH 011/296] 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 Signed-off-by: anupritaisno1 --- include/linux/fsnotify.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 478050b5c2a95..1fb740fad2429 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -237,11 +237,19 @@ static inline void fsnotify_open(struct file *file) { struct path *path = &file->f_path; struct inode *inode = path->dentry->d_inode; + struct path lower_path; + __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); } From e8abf1fa9a4ee10adca18d8a01ce491d2f2e16ba Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 1 Feb 2018 16:52:22 -0800 Subject: [PATCH 012/296] 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 Signed-off-by: anupritaisno1 --- 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 75beeff149f0b..46af20db62c81 100755 --- 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 f949fcd0ce928..512d8d0e57a4e 100755 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -348,13 +348,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 31c013d28cc45..641b1ec59e62b 100755 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -200,6 +200,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; @@ -379,7 +380,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); @@ -401,15 +407,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, @@ -515,8 +526,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 87d6f836592e8..cffcdb11cb8ac 100755 --- 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; From dc8fff6d4f0128a2ad61ff565575ffc9aa7c6f98 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 20 Feb 2018 20:25:45 -0800 Subject: [PATCH 013/296] 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 Signed-off-by: anupritaisno1 --- fs/sdcardfs/file.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index c560360f25c07..72338cc3d322c 100755 --- 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 = 0; 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, - lower_file->f_path.dentry->d_inode); - fsstack_copy_attr_times(dentry->d_inode, - lower_file->f_path.dentry->d_inode); + 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; From 32589aad7db0ff948a9558576d6e463aef8d2f3e Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 4 Dec 2017 09:51:07 +0530 Subject: [PATCH 014/296] ANDROID: sdcardfs: Set num in extension_details during make_item Without this patch when you delete an extension from configfs it still exists in the hash table data structures and we are unable to delete it or change it's group. This happens because during deletion the key & value is taken from extension_details, and was not properly set. Fix it by this patch. Change-Id: I7c20cb1ab4d99e6aceadcb5ef850f0bb47f18be8 Signed-off-by: Ritesh Harjani Signed-off-by: Daniel Rosenberg Bug: 73055997 Signed-off-by: anupritaisno1 --- fs/sdcardfs/packagelist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 886d2f699b804..d9f8ed4961b9f 100755 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -645,6 +645,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou return ERR_PTR(-ENOMEM); } qstr_init(&extension_details->name, tmp); + extension_details->num = extensions_value->num; ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num); if (ret) { From 0a2bd2209263384b5bb52f03da56b2b6583bb2c1 Mon Sep 17 00:00:00 2001 From: OzzysCmAcc Date: Wed, 28 Mar 2018 20:33:10 +0200 Subject: [PATCH 015/296] Revert "msm: vidc: Allow clients to specify/query the colorspace" This reverts commit d43fa694c0f38d5ecc88ac04c0a54903a3239c12. Change-Id: I315e206b228f828c1a2e0ff5c5c15260a39b4774 Signed-off-by: anupritaisno1 --- .../platform/msm/vidc/hfi_packetization.c | 42 -------------- drivers/media/platform/msm/vidc/msm_vdec.c | 11 ++-- drivers/media/platform/msm/vidc/msm_venc.c | 56 +------------------ .../media/platform/msm/vidc/msm_vidc_common.c | 3 - drivers/media/platform/msm/vidc/vidc_hfi.h | 2 - .../media/platform/msm/vidc/vidc_hfi_api.h | 12 ---- .../media/platform/msm/vidc/vidc_hfi_helper.h | 12 +--- include/uapi/linux/v4l2-controls.h | 1 - include/uapi/media/msm_vidc.h | 18 ------ 9 files changed, 8 insertions(+), 149 deletions(-) diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index 4f1827a2b727b..7808497508c5a 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -493,9 +493,6 @@ static int get_hfi_extradata_index(enum hal_extradata_id index) case HAL_EXTRADATA_METADATA_MBI: ret = HFI_PROPERTY_PARAM_VENC_MBI_DUMPING; break; - case HAL_EXTRADATA_VUI_DISPLAY_INFO: - ret = HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA; - break; default: dprintk(VIDC_WARN, "Extradata index not found: %d\n", index); break; @@ -1863,7 +1860,6 @@ int create_pkt_cmd_session_set_property( sizeof(struct hfi_hybrid_hierp); break; } - case HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED: { create_pkt_enable(pkt->rg_property_data, @@ -1872,44 +1868,6 @@ int create_pkt_cmd_session_set_property( pkt->size += sizeof(u32) + sizeof(struct hfi_enable); break; } - - case HAL_PARAM_VENC_VIDEO_SIGNAL_INFO: - { - u32 color_space, matrix_coeffs, transfer_chars; - struct hal_video_signal_info *hal = pdata; - struct hfi_video_signal_metadata *signal_info = - (struct hfi_video_signal_metadata *) - &pkt->rg_property_data[1]; - - switch (hal->color_space) { - /* See colour_primaries of ISO/IEC 14496 for significance */ - case HAL_VIDEO_COLOR_SPACE_601: - color_space = 5; - transfer_chars = 6; - matrix_coeffs = 5; - break; - case HAL_VIDEO_COLOR_SPACE_709: - color_space = 1; - transfer_chars = 1; - matrix_coeffs = 1; - break; - default: - return -ENOTSUPP; - } - - signal_info->enable = true; - signal_info->video_format = 5; - signal_info->video_full_range = !hal->clamped; - signal_info->color_description = 1; - signal_info->color_primaries = color_space; - signal_info->transfer_characteristics = transfer_chars; - signal_info->matrix_coeffs = matrix_coeffs; - - pkt->rg_property_data[0] = - HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO; - pkt->size += sizeof(u32) + sizeof(*signal_info); - break; - } /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ case HAL_CONFIG_BUFFER_REQUIREMENTS: case HAL_CONFIG_PRIORITY: diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index db6ae229c687c..d9327740a7391 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -62,6 +62,8 @@ static const char *const mpeg_video_vidc_extradata[] = { "Extradata Frame Rate", "Extradata Panscan Window", "Extradata Recovery point SEI", + "Extradata Closed Caption UD", + "Extradata AFD UD", "Extradata Multislice info", "Extradata number of concealed MB", "Extradata metadata filler", @@ -69,10 +71,6 @@ static const char *const mpeg_video_vidc_extradata[] = { "Extradata digital zoom", "Extradata aspect ratio", "Extradata mpeg2 seqdisp", - "Extradata UD", - "Extradata frame QP", - "Extradata frame bits info", - "Extradata display VUI", }; static const char *const mpeg_vidc_video_alloc_mode_type[] = { "Buffer Allocation Static", @@ -249,7 +247,7 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { .name = "Extradata Type", .type = V4L2_CTRL_TYPE_MENU, .minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE, - .maximum = V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY, + .maximum = V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO, .default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE, .menu_skip_mask = ~( (1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) | @@ -271,8 +269,7 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP) | (1 << V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA) | (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP) | - (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO) | - (1 << V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY) + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO) ), .qmenu = mpeg_video_vidc_extradata, .step = 0, diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 4c818877af218..a694fe6dd3c14 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -3202,7 +3202,6 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { struct hal_uncompressed_format_select hal_fmt = {0}; struct hal_frame_size frame_sz; - struct hal_video_signal_info signal_info = {0}; inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width; inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height; @@ -3214,7 +3213,6 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) goto exit; } - /* Configure frame dimensions */ frame_sz.buffer_type = HAL_BUFFER_INPUT; frame_sz.width = inst->prop.width[OUTPUT_PORT]; frame_sz.height = inst->prop.height[OUTPUT_PORT]; @@ -3237,7 +3235,6 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) goto exit; } - /* Configure frame color format */ fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats, ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat, OUTPUT_PORT); @@ -3271,54 +3268,6 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) "Failed to set input color format\n"); goto exit; } - - /* Configure frame color format characteristics */ - if (f->fmt.pix_mp.colorspace) { - switch (f->fmt.pix_mp.colorspace) { - case V4L2_COLORSPACE_REC709: - signal_info.color_space = - HAL_VIDEO_COLOR_SPACE_709; - signal_info.clamped = true; - break; - case V4L2_COLORSPACE_BT878: - /* equiv to ITU-R BT.601 clamped */ - signal_info.clamped = true; - /* fall thru */ - case V4L2_COLORSPACE_470_SYSTEM_BG: - /* equiv to ITU-R BT.601 */ - signal_info.color_space = - HAL_VIDEO_COLOR_SPACE_601; - break; - default: - dprintk(VIDC_ERR, "Colorspace %d not supported\n", - f->fmt.pix_mp.colorspace); - rc = -ENOTSUPP; - goto exit; - } - - switch (f->fmt.pix_mp.pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - rc = call_hfi_op(hdev, session_set_property, - inst->session, - HAL_PARAM_VENC_VIDEO_SIGNAL_INFO, - &signal_info); - if (rc) { - dprintk(VIDC_ERR, - "Failed to set the colorspace: %d\n", - rc); - goto exit; - } - break; - default: - dprintk(VIDC_ERR, - "Colorspace %d not supported for format %d\n", - f->fmt.pix_mp.colorspace, - f->fmt.pix_mp.pixelformat); - rc = -ENOTSUPP; - break; - } - } } if (!fmt) { @@ -3350,8 +3299,9 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) frame_sz.width = inst->prop.width[CAPTURE_PORT]; frame_sz.height = inst->prop.height[CAPTURE_PORT]; frame_sz.buffer_type = HAL_BUFFER_OUTPUT; - rc = call_hfi_op(hdev, session_set_property, inst->session, - HAL_PARAM_FRAME_SIZE, &frame_sz); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, + &frame_sz); if (rc) { dprintk(VIDC_ERR, "Failed to set OUTPUT framesize\n"); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 3eaa3ba26736c..40dd791bdff65 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -3947,9 +3947,6 @@ enum hal_extradata_id msm_comm_get_hal_extradata_index( case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI: ret = HAL_EXTRADATA_METADATA_MBI; break; - case V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY: - ret = HAL_EXTRADATA_VUI_DISPLAY_INFO; - break; default: dprintk(VIDC_WARN, "Extradata not found: %d\n", index); break; diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h index 5c9c4b2a9bfa8..8eefc7cee6160 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi.h @@ -212,8 +212,6 @@ struct hfi_extradata_header { (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x019) #define HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD \ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01A) -#define HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA \ - (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01B) #define HFI_PROPERTY_CONFIG_VDEC_OX_START \ (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000) diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 482809de3ebe5..eda66d35de98b 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -114,7 +114,6 @@ enum hal_extradata_id { HAL_EXTRADATA_DIGITAL_ZOOM, HAL_EXTRADATA_LTR_INFO, HAL_EXTRADATA_METADATA_MBI, - HAL_EXTRADATA_VUI_DISPLAY_INFO, }; enum hal_property { @@ -216,7 +215,6 @@ enum hal_property { HAL_PARAM_VDEC_NON_SECURE_OUTPUT2, HAL_PARAM_VENC_HIER_P_HYBRID_MODE, HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED, - HAL_PARAM_VENC_VIDEO_SIGNAL_INFO, }; enum hal_domain { @@ -927,16 +925,6 @@ struct hal_vpe_color_space_conversion { u32 csc_limit[HAL_MAX_LIMIT_COEFFS]; }; -enum hal_video_color_space { - HAL_VIDEO_COLOR_SPACE_601, - HAL_VIDEO_COLOR_SPACE_709, -}; - -struct hal_video_signal_info { - enum hal_video_color_space color_space; - bool clamped; -}; - enum vidc_resource_id { VIDC_RESOURCE_OCMEM = 0x00000001, VIDC_UNUSED_RESORUCE = 0x10000000, diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index 13ed39410a660..2c60d0e9d04e9 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -338,7 +338,7 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B) #define HFI_PROPERTY_PARAM_VENC_LTRMODE \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01C) -#define HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO \ +#define HFI_PROPERTY_PARAM_VENC_VIDEO_FULL_RANGE \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01D) #define HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01E) @@ -628,16 +628,6 @@ struct hfi_frame_size { u32 height; }; -struct hfi_video_signal_metadata { - u32 enable; - u32 video_format; - u32 video_full_range; - u32 color_description; - u32 color_primaries; - u32 transfer_characteristics; - u32 matrix_coeffs; -}; - struct hfi_h264_vui_timing_info { u32 enable; u32 fixed_frame_rate; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index cedad8eb14755..4f944f5ba66ee 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -739,7 +739,6 @@ enum v4l2_mpeg_vidc_extradata { V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO = 19, V4L2_MPEG_VIDC_EXTRADATA_LTR = 20, V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI = 21, - V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY = 22, }; #define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26) diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h index dc2fa5724f9b6..6326030eb6e64 100644 --- a/include/uapi/media/msm_vidc.h +++ b/include/uapi/media/msm_vidc.h @@ -109,23 +109,6 @@ struct msm_vidc_s3d_frame_packing_payload { unsigned int fpa_extension_flag; }; -struct msm_vidc_vui_display_info_payload { - unsigned int video_signal_present_flag; - unsigned int video_format; - unsigned int bit_depth_y; - unsigned int bit_depth_c; - unsigned int video_full_range_flag; - unsigned int color_description_present_flag; - unsigned int color_primaries; - unsigned int transfer_characteristics; - unsigned int matrix_coefficients; - unsigned int chroma_location_info_present_flag; - unsigned int chroma_format_idc; - unsigned int separate_color_plane_flag; - unsigned int chroma_sample_loc_type_top_field; - unsigned int chroma_sample_loc_type_bottom_field; -}; - enum msm_vidc_extradata_type { MSM_VIDC_EXTRADATA_NONE = 0x00000000, MSM_VIDC_EXTRADATA_MB_QUANTIZATION = 0x00000001, @@ -150,7 +133,6 @@ enum msm_vidc_extradata_type { MSM_VIDC_EXTRADATA_METADATA_LTR = 0x7F100004, MSM_VIDC_EXTRADATA_METADATA_FILLER = 0x7FE00002, MSM_VIDC_EXTRADATA_METADATA_MBI = 0x7F100005, - MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO = 0x7F100006, }; enum msm_vidc_interlace_type { MSM_VIDC_INTERLACE_FRAME_PROGRESSIVE = 0x01, From bf5f3db24f4d2cb714b2c549ac64a5985cbc335e Mon Sep 17 00:00:00 2001 From: Oskar Kratochvil Date: Fri, 30 Mar 2018 07:13:44 +0000 Subject: [PATCH 016/296] Revert "camera_v2: isp: Replace monotonic timestamp with boot_time" This reverts commit 91bca269f649061eb5a54510f466d06c3b5ca1fb. Change-Id: Ib298dafdcfad95f6815c46bb80202267bd23d485 Signed-off-by: anupritaisno1 --- drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 306d6db7471f7..d1315068515c3 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 @@ -334,9 +334,9 @@ int msm_isp_get_clk_info(struct vfe_device *vfe_dev, static inline void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp) { struct timespec ts; - get_monotonic_boottime(&ts); - time_stamp->buf_time.tv_sec = ts.tv_sec; - time_stamp->buf_time.tv_usec = ts.tv_nsec/1000; + ktime_get_ts(&ts); + time_stamp->buf_time.tv_sec = ts.tv_sec; + time_stamp->buf_time.tv_usec = ts.tv_nsec/1000; do_gettimeofday(&(time_stamp->event_time)); } From 5753182e48bc2ee0da5f90e89fbb60f5392b9b3e Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 31 Mar 2018 15:25:39 +0000 Subject: [PATCH 017/296] Revert "BACKPORT: net: xfrm: support setting an output mark." This reverts commit c688d2cdf0245bb69d32d7d0d4941099af1e15cb. Change-Id: I90508f23fcacfe9de6eb256affdd3ff839051b05 Signed-off-by: anupritaisno1 --- net/xfrm/xfrm_user.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 0ba7cf4093146..e1e595651894a 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -863,11 +863,6 @@ 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; } From f3cefb8e7fd2d7506c37e8788f5498ac90f92352 Mon Sep 17 00:00:00 2001 From: Dan Elkouby Date: Sun, 6 Mar 2016 11:19:48 +0200 Subject: [PATCH 018/296] tri_state_key: allow more key codes for extra modes Change-Id: I2ab8557a9d10b88ef5d0bf0aa55e00b8fb67200a Signed-off-by: anupritaisno1 --- drivers/input/misc/tri_state_key.c | 40 +++++++++++------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/input/misc/tri_state_key.c b/drivers/input/misc/tri_state_key.c index ebc4655b4f9e0..5a104f29029fd 100755 --- a/drivers/input/misc/tri_state_key.c +++ b/drivers/input/misc/tri_state_key.c @@ -36,23 +36,14 @@ 4�ź�3������ 1 0 | Normal */ -typedef enum { - MODE_UNKNOWN, - MODE_MUTE, - MODE_DO_NOT_DISTURB, - MODE_NORMAL, - MODE_MAX_NUM -} tri_mode_t; - -#define MODE_TOTAL_SILENCE 600 -#define MODE_ALARMS_ONLY 601 -#define MODE_PRIORITY_ONLY 602 -#define MODE_NONE 603 + +#define KEYCODE_BASE 600 +#define TOTAL_KEYCODES 6 static int current_mode = 0; -static int keyCode_slider_top = MODE_ALARMS_ONLY; -static int keyCode_slider_middle = MODE_PRIORITY_ONLY; -static int keyCode_slider_bottom = MODE_NONE; +static int keyCode_slider_top = KEYCODE_BASE + 1; +static int keyCode_slider_middle = KEYCODE_BASE + 2; +static int keyCode_slider_bottom = KEYCODE_BASE + 3; struct switch_dev_data { //tri_mode_t last_type; @@ -103,17 +94,17 @@ static void switch_dev_work(struct work_struct *work) mutex_lock(&sem); if(!gpio_get_value(switch_data->key2_gpio)) { - mode = MODE_NORMAL; + mode = 3; keyCode = keyCode_slider_bottom; } else if(gpio_get_value(switch_data->key1_gpio)) { - mode = MODE_DO_NOT_DISTURB; + mode = 2; keyCode = keyCode_slider_middle; } else { - mode = MODE_MUTE; + mode = 1; keyCode = keyCode_slider_top; } if (current_mode != mode) { @@ -319,7 +310,7 @@ static ssize_t keyCode_top_write(struct file *file, const char __user *page, siz if (sscanf(buf, "%d", &data) != 1) return t; - if (data < 600 || data > 603) + if (data < KEYCODE_BASE || data >= (KEYCODE_BASE + TOTAL_KEYCODES)) return t; keyCode_slider_top = data; @@ -363,7 +354,7 @@ static ssize_t keyCode_middle_write(struct file *file, const char __user *page, if (sscanf(buf, "%d", &data) != 1) return t; - if (data < 600 || data > 603) + if (data < KEYCODE_BASE || data >= (KEYCODE_BASE + TOTAL_KEYCODES)) return t; keyCode_slider_middle = data; @@ -407,7 +398,7 @@ static ssize_t keyCode_bottom_write(struct file *file, const char __user *page, if (sscanf(buf, "%d", &data) != 1) return t; - if (data < 600 || data > 603) + if (data < KEYCODE_BASE || data >= (KEYCODE_BASE + TOTAL_KEYCODES)) return t; keyCode_slider_bottom = data; @@ -437,6 +428,7 @@ static int tristate_dev_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct proc_dir_entry *procdir; int error=0; + int i; //void __iomem *cfg_reg; @@ -464,10 +456,8 @@ static int tristate_dev_probe(struct platform_device *pdev) switch_data->input->name = DRV_NAME; switch_data->input->dev.parent = &pdev->dev; set_bit(EV_KEY, switch_data->input->evbit); - set_bit(MODE_TOTAL_SILENCE, switch_data->input->keybit); - set_bit(MODE_ALARMS_ONLY, switch_data->input->keybit); - set_bit(MODE_PRIORITY_ONLY, switch_data->input->keybit); - set_bit(MODE_NONE, switch_data->input->keybit); + for (i = KEYCODE_BASE; i < KEYCODE_BASE + TOTAL_KEYCODES; i++) + set_bit(i, switch_data->input->keybit); input_set_drvdata(switch_data->input, switch_data); error = input_register_device(switch_data->input); if (error) { From 6a5fa354a1d3c734938b5b9f3abf24147f1c1cd2 Mon Sep 17 00:00:00 2001 From: Scott Mertz Date: Tue, 4 Aug 2015 14:26:46 -0700 Subject: [PATCH 019/296] leds: leds-qpnp: add synchronized rgb control Change-Id: I0833d298a6568cb490ac87561e884552cb1d0488 Signed-off-by: anupritaisno1 --- drivers/leds/leds-qpnp.c | 176 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c index b0dcfe7845a46..71949d5c82993 100644 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -267,6 +267,8 @@ enum qpnp_leds { QPNP_ID_MAX, }; +#define QPNP_ID_TO_RGB_IDX(id) (id - QPNP_ID_RGB_RED) + /* current boost limit */ enum wled_current_boost_limit { WLED_CURR_LIMIT_105mA, @@ -556,6 +558,15 @@ struct qpnp_led_data { int turn_off_delay_ms; }; +/** + * struct rgb_sync - rgb led synchrnize structure + */ +struct rgb_sync { + struct led_classdev cdev; + struct spmi_device *spmi_dev; + struct qpnp_led_data *led_data[3]; +}; + static DEFINE_MUTEX(flash_lock); static struct pwm_device *kpdbl_master; static u32 kpdbl_master_period_us; @@ -2690,6 +2701,130 @@ static ssize_t blink_store(struct device *dev, return count; } +static inline void rgb_lock_leds(struct rgb_sync *rgb) +{ + int i; + + for (i = 0; i < 3; i++) { + if (rgb->led_data[i]) { + flush_work(&rgb->led_data[i]->work); + mutex_lock(&rgb->led_data[i]->lock); + } + } +} + +static inline void rgb_unlock_leds(struct rgb_sync *rgb) +{ + int i; + + for (i = 0; i < 3; i++) { + if (rgb->led_data[i]) { + mutex_unlock(&rgb->led_data[i]->lock); + } + } +} + +static void rgb_disable_leds(struct rgb_sync *rgb) +{ + int i; + struct qpnp_led_data *led; + + //TODO Implement synchronized off + for (i = 0; i < 3; i++) { + led = rgb->led_data[i]; + if (led && led->rgb_cfg->pwm_cfg->pwm_enabled) { + led->rgb_cfg->pwm_cfg->mode = + led->rgb_cfg->pwm_cfg->default_mode; + led->rgb_cfg->pwm_cfg->blinking = false; + pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); + led->rgb_cfg->pwm_cfg->pwm_enabled = 0; + } + } +} + +/** + * Should only be called when all RGB leds are off + */ +static int rgb_enable_leds(struct rgb_sync *rgb) +{ + struct qpnp_led_data *led; + struct pwm_device *pwm_dev[3]; + int i, rc; + + for (i = 0; i < 3; i++) { + led = rgb->led_data[i]; + if (!led) + continue; + + led->rgb_cfg->pwm_cfg->mode = LPG_MODE; + pwm_free(led->rgb_cfg->pwm_cfg->pwm_dev); + qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev, led->cdev.name); + pwm_dev[i] = led->rgb_cfg->pwm_cfg->pwm_dev; + } + + if (i == 0) + return 0; + + rc = pwm_enable_synchronized(pwm_dev, i); + if (rc) { + dev_err(&rgb->spmi_dev->dev, "Unable to enable pwms\n"); + return rc; + } + + for (i = 0; i < 3; i++) { + led = rgb->led_data[i]; + if (!led) + continue; + led->rgb_cfg->pwm_cfg->blinking = true; + led->rgb_cfg->pwm_cfg->pwm_enabled = 1; + } + + return rc; +} + +static ssize_t rgb_blink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rgb_sync *rgb_sync; + struct qpnp_led_data *led; + unsigned long blinking; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t rc = -EINVAL, i; + u8 enable = 0; + + rc = kstrtoul(buf, 10, &blinking); + if (rc) + return rc; + rgb_sync = container_of(led_cdev, struct rgb_sync, cdev); + + rgb_lock_leds(rgb_sync); + for (i = 0; i < 3; i++) { + if (rgb_sync->led_data[i]) { + led = rgb_sync->led_data[i]; + enable |= led->rgb_cfg->enable; + } + } + + if (!led) + return count; + + rc = qpnp_led_masked_write(led, + RGB_LED_EN_CTL(led->base), + enable, blinking ? enable : RGB_LED_DISABLE); + if (rc) { + dev_err(&led->spmi_dev->dev, + "Failed to write led enable reg\n"); + rgb_unlock_leds(rgb_sync); + return rc; + } + rgb_disable_leds(rgb_sync); + if (blinking) + rgb_enable_leds(rgb_sync); + rgb_unlock_leds(rgb_sync); + return count; +} + static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store); static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store); static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store); @@ -2700,6 +2835,7 @@ static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store); static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store); static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store); static DEVICE_ATTR(blink, 0664, NULL, blink_store); +static DEVICE_ATTR(rgb_blink, 0664, NULL, rgb_blink_store); static struct attribute *led_attrs[] = { &dev_attr_led_mode.attr, @@ -2731,6 +2867,11 @@ static struct attribute *blink_attrs[] = { NULL }; +static struct attribute *rgb_blink_attrs[] = { + &dev_attr_rgb_blink.attr, + NULL +}; + static const struct attribute_group pwm_attr_group = { .attrs = pwm_attrs, }; @@ -2743,6 +2884,10 @@ static const struct attribute_group blink_attr_group = { .attrs = blink_attrs, }; +static const struct attribute_group rgb_blink_attr_group = { + .attrs = rgb_blink_attrs, +}; + static int qpnp_flash_init(struct qpnp_led_data *led) { int rc; @@ -3861,6 +4006,7 @@ static int qpnp_leds_probe(struct spmi_device *spmi) int rc, i, num_leds = 0, parsed_leds = 0; const char *led_label; bool regulator_probe = false; + struct rgb_sync *rgb_sync = NULL; node = spmi->dev.of_node; if (node == NULL) @@ -3880,6 +4026,29 @@ static int qpnp_leds_probe(struct spmi_device *spmi) return -ENOMEM; } + if (of_property_read_bool(node, "qcom,rgb-sync")) { + rgb_sync = devm_kzalloc(&spmi->dev, + sizeof(struct rgb_sync), GFP_KERNEL); + if (!rgb_sync) { + dev_err(&spmi->dev, "Unable to allocate memory\n"); + kfree(led_array); + return -ENOMEM; + } + rgb_sync->cdev.name = "rgb"; + rgb_sync->spmi_dev = spmi; + rc = led_classdev_register(&spmi->dev, &rgb_sync->cdev); + if (rc) { + dev_err(&spmi->dev, "unable to register rgb %d\n", rc); + goto fail_id_check; + } + rc = sysfs_create_group(&rgb_sync->cdev.dev->kobj, + &rgb_blink_attr_group); + if (rc) { + dev_err(&spmi->dev, "unable to create rgb sysfs %d\n", rc); + goto fail_id_check; + } + } + for_each_child_of_node(node, temp) { led = &led_array[parsed_leds]; led->num_leds = num_leds; @@ -4084,6 +4253,9 @@ static int qpnp_leds_probe(struct spmi_device *spmi) &lpg_attr_group); if (rc) goto fail_id_check; + + if (rgb_sync) + rgb_sync->led_data[QPNP_ID_TO_RGB_IDX(led->id)] = led; } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) { rc = sysfs_create_group(&led->cdev.dev->kobj, &lpg_attr_group); @@ -4130,6 +4302,10 @@ static int qpnp_leds_probe(struct spmi_device *spmi) return 0; fail_id_check: + if (rgb_sync) { + led_classdev_unregister(&rgb_sync->cdev); + kfree(rgb_sync); + } for (i = 0; i < parsed_leds; i++) { if (led_array[i].id != QPNP_ID_FLASH1_LED0 && led_array[i].id != QPNP_ID_FLASH1_LED1) From 6be1688e2188ec57bc2adafdc3af89c7561d207b Mon Sep 17 00:00:00 2001 From: Scott Mertz Date: Tue, 4 Aug 2015 02:25:19 -0700 Subject: [PATCH 020/296] pwm: qpnp-pwm: add api for synchronous enable of pwms Change-Id: I29d8a2a3dc7adff734bdbbd0e9969323b86e461f Signed-off-by: anupritaisno1 --- drivers/pwm/pwm-qpnp.c | 125 +++++++++++++++++++++++++++++++++++++++ include/linux/qpnp/pwm.h | 5 ++ 2 files changed, 130 insertions(+) diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c index 0ab6af3e59b53..cee4bfa0825db 100644 --- a/drivers/pwm/pwm-qpnp.c +++ b/drivers/pwm/pwm-qpnp.c @@ -1110,6 +1110,88 @@ static int qpnp_lpg_configure_lut_state(struct qpnp_pwm_chip *chip, return rc; } +static int qpnp_lpg_configure_lut_states(struct qpnp_pwm_chip **chips, + int num, enum qpnp_lut_state state) +{ + struct qpnp_pwm_chip *chip; + struct qpnp_lpg_config *lpg_config; + u8 value1, value2, mask1, mask2; + u8 *reg1, *reg2; + u16 addr, addr1; + int rc, i; + bool test_enable; + u8 ramp_en = 0, ramp_mask = 0; + + + for (i = 0; i < num; i++) { + chip = chips[i]; + lpg_config = &chip->lpg_config; + value1 = chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL]; + reg1 = &chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL]; + reg2 = &chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]; + mask2 = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK | + QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK; + if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE) + mask2 |= QPNP_EN_PWM_OUTPUT_MASK; + + if (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE + && chip->revision == QPNP_LPG_REVISION_0) { + if (state == QPNP_LUT_ENABLE) { + QPNP_ENABLE_LUT_V0(value1); + value2 = QPNP_ENABLE_LPG_MODE(chip); + } else { + QPNP_DISABLE_LUT_V0(value1); + value2 = QPNP_DISABLE_LPG_MODE(chip); + } + mask1 = QPNP_RAMP_START_MASK; + addr1 = SPMI_LPG_REG_ADDR(lpg_config->base_addr, + QPNP_RAMP_CONTROL); + } else if ((chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE + && chip->revision == QPNP_LPG_REVISION_1) + || chip->sub_type == QPNP_LPG_S_CHAN_SUB_TYPE) { + if (state == QPNP_LUT_ENABLE) { + QPNP_ENABLE_LUT_V1(value1, + lpg_config->lut_config.ramp_index); + value2 = QPNP_ENABLE_LPG_MODE(chip); + } else { + value2 = QPNP_DISABLE_LPG_MODE(chip); + } + mask1 = value1; + addr1 = lpg_config->lut_base_addr + + SPMI_LPG_REV1_RAMP_CONTROL_OFFSET; + } else { + pr_err("Unsupported LPG subtype 0x%02x, revision 0x%02x\n", + chip->sub_type, chip->revision); + return -EINVAL; + } + + addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr, + QPNP_ENABLE_CONTROL); + + if (chip->in_test_mode) { + test_enable = (state == QPNP_LUT_ENABLE) ? 1 : 0; + rc = qpnp_dtest_config(chip, test_enable); + if (rc) + pr_err("Failed to configure TEST mode\n"); + } + + rc = qpnp_lpg_save_and_write(value2, mask2, reg2, + addr, 1, chip); + if (rc) + return rc; + + ramp_en |= value1; + ramp_mask |= mask1; + } + + if (state == QPNP_LUT_ENABLE + || (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE + && chip->revision == QPNP_LPG_REVISION_0)) + rc = qpnp_lpg_save_and_write(ramp_en, ramp_mask, reg1, + addr1, 1, chip); + return rc; +} + static inline int qpnp_enable_pwm_mode(struct qpnp_pwm_chip *chip) { if (chip->pwm_config.supported_sizes == QPNP_PWM_SIZE_7_8_BIT) @@ -1371,6 +1453,49 @@ static int qpnp_pwm_enable(struct pwm_chip *pwm_chip, return rc; } +int pwm_enable_synchronized(struct pwm_device **pwms, size_t num) +{ + unsigned long *flags; + struct qpnp_pwm_chip **chips; + int rc = 0, i; + + if (pwms == NULL || IS_ERR(pwms) || num == 0) { + pr_err("Invalid pwm handle or idx_len=0\n"); + return -EINVAL; + } + + flags = kzalloc(sizeof(unsigned long) * num, GFP_KERNEL); + if (!flags) + return -ENOMEM; + + chips = kzalloc(sizeof(struct qpnp_pwm_chip *) * num, GFP_KERNEL); + if (!chips) { + kfree(flags); + return -ENOMEM; + } + + for (i = 0; i < num; i++) { + chips[i] = qpnp_pwm_from_pwm_dev(pwms[i]); + if (chips[i] == NULL) { + rc = -EINVAL; + goto err_failed; + } + spin_lock_irqsave(&chips[i]->lpg_lock, flags[i]); + } + + rc = qpnp_lpg_configure_lut_states(chips, num, QPNP_LUT_ENABLE); + +err_failed: + while(i > 0) { + spin_unlock_irqrestore(&chips[i - 1]->lpg_lock, flags[i - 1]); + i--; + } + kfree(flags); + kfree(chips); + return rc; +} +EXPORT_SYMBOL_GPL(pwm_enable_synchronized); + /** * qpnp_pwm_disable - stop a PWM output toggling * @pwm_chip: the PWM chip diff --git a/include/linux/qpnp/pwm.h b/include/linux/qpnp/pwm.h index 50fb2e52a225e..3d8de231545ec 100644 --- a/include/linux/qpnp/pwm.h +++ b/include/linux/qpnp/pwm.h @@ -152,6 +152,11 @@ int pwm_lut_config(struct pwm_device *pwm, int period_us, int pwm_config_us(struct pwm_device *pwm, int duty_us, int period_us); +/* + * synchronized enable of multiple pwm instances + */ +int pwm_enable_synchronized(struct pwm_device **pwms, size_t num); + /* Standard APIs supported */ /* * pwm_request - request a PWM device From 34f6d62ad898155d73bb36c10a9d8301ffe5e03d Mon Sep 17 00:00:00 2001 From: Dan Elkouby Date: Mon, 30 Jan 2017 19:16:31 +0200 Subject: [PATCH 021/296] ARM: dts: pmi8994: add synchronized RGB blink params Change-Id: Ic43b0fcd869d12768b2b421d2e235cfbf32b7cba Signed-off-by: anupritaisno1 --- arch/arm/boot/dts/qcom/msm-pmi8994.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi index 94da8ca6e1b03..76dde833f6893 100644 --- a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi @@ -608,6 +608,8 @@ label = "rgb"; status = "okay"; + qcom,rgb-sync; + qcom,rgb_0 { label = "rgb"; qcom,id = <3>; From 8da3f585fcb29a902595de1ae634072ec55160f9 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Tue, 26 Sep 2017 14:16:32 -0400 Subject: [PATCH 022/296] wcnss: fix 3 byte stack buffer overflow Change-Id: I0c47844e47d0396e4f241d4472e904b1ee7dc1bc Signed-off-by: anupritaisno1 --- drivers/net/wireless/wcnss/wcnss_wlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index 5a714ba555327..643682f4c876c 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -198,7 +198,7 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2) #define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3) -#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ADDRESS_STR "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx" /* message types */ #define WCNSS_CTRL_MSG_START 0x01000000 From 8bba72053781f7d6b7c0a4cfe114deba53bfc227 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Mon, 11 Dec 2017 14:44:39 +0530 Subject: [PATCH 023/296] 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 Bug: 68664502 Change-Id: Ifd011dd85dd9f610c7b69dd460f73d26e006cd66 Signed-off-by: Aditya Bavanari Signed-off-by: anupritaisno1 --- sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 32 ++++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index d467464f4e67d..bd8295448c75a 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -108,6 +108,7 @@ struct msm_compr_pdata { bool use_dsp_gapless_mode; 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 { @@ -921,11 +922,16 @@ static int msm_compr_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"); @@ -937,7 +943,7 @@ static int msm_compr_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); @@ -945,10 +951,11 @@ static int msm_compr_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; @@ -958,7 +965,9 @@ static int msm_compr_open(struct snd_compr_stream *cstream) if (!prtd->audio_client) { 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->dec_params[rtd->dai_link->be_id] = NULL; pdata->cstream[rtd->dai_link->be_id] = NULL; kfree(prtd); return -ENOMEM; @@ -1016,7 +1025,7 @@ static int msm_compr_open(struct snd_compr_stream *cstream) } else { pr_err("%s: Unsupported stream type", __func__); } - + pdata->is_in_use[rtd->dai_link->be_id] = true; return 0; } @@ -1111,11 +1120,15 @@ static int msm_compr_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; @@ -2577,6 +2590,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; } /* From 4e31a18689c1001a63094ea9c2ebf4cd9948ee78 Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Tue, 19 Dec 2017 19:15:19 +0200 Subject: [PATCH 024/296] 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. Bug: 72957269 Change-Id: Ic813433655411f1447db8b0c15efdf64038d8c26 CRs-fixed: 2151146 Signed-off-by: Ghanim Fodi Signed-off-by: anupritaisno1 --- drivers/platform/msm/ipa/ipa_hdr.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_hdr.c index 702771a0080ba..3f33d9138ee66 100644 --- a/drivers/platform/msm/ipa/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_hdr.c @@ -809,8 +809,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); @@ -1113,8 +1122,19 @@ int ipa_reset_hdr(void) &ipa_ctx->hdr_tbl.head_hdr_entry_list, link) { /* do not remove the default header */ - if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) - continue; + if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { + 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); + return -EFAULT; + } + IPADBG("skip default header\n"); + continue; + } + } if (ipa_id_find(entry->id) == NULL) { WARN_ON(1); From 1caea2662cb18a0cad3dbf73f06b8c6833a9b06e Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Tue, 19 Dec 2017 20:34:26 +0530 Subject: [PATCH 025/296] ASoC: apr: Add validity check to APR port Add boundary checks for APR port received from ADSP. CRs-Fixed: 2143207 Bug: 72956842 Change-Id: I9a7fa39ee223e1859323caa6eb74c1c8a26a041d Signed-off-by: Aditya Bavanari Signed-off-by: anupritaisno1 --- 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 ae6acb81018e4..cb4cda4f7f313 100644 --- a/drivers/soc/qcom/qdsp6v2/apr.c +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -551,7 +551,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); From 4fd92cc9ad33977a6d52ef1880ed63793e3a7d7d Mon Sep 17 00:00:00 2001 From: Jonathan Solnit Date: Mon, 12 Feb 2018 14:41:49 -0800 Subject: [PATCH 026/296] BACKPORT: 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. Bug: 71500434 Change-Id: Ic02fa0f7b8f8525739996be2e0309ad2fa5b97dc 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 Signed-off-by: Jonathan Solnit Signed-off-by: anupritaisno1 # Conflicts: # net/ipv4/raw.c --- 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 b4e9806af950c..ead11d28b5087 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -476,11 +476,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. */ @@ -551,7 +556,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) @@ -573,13 +578,13 @@ 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) | FLOWI_FLAG_CAN_SLEEP | - (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), + (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), daddr, saddr, 0, 0, sock_i_uid(sk)); - if (!inet->hdrincl) { + if (!hdrincl) { err = raw_probe_proto_opt(&fl4, msg); if (err) goto done; @@ -601,7 +606,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); From f39bb1b8086581b48104cc3094e90d116eff7b37 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 12 Feb 2018 17:38:47 -0800 Subject: [PATCH 027/296] UPSTREAM: KEYS: encrypted: fix buffer overread in valid_master_desc() 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: linux-stable # 3.18.y Cc: Mimi Zohar Bug: 70526974 Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: James Morris Signed-off-by: Jin Qian Signed-off-by: Steve Pfetsch Change-Id: I2cc3af94f855e66f2014dd1dced4425ed8a41f29 (cherry picked from commit 794b4bc292f5d31739d89c0202c54e7dc9bc3add) Signed-off-by: anupritaisno1 --- 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 419442cc36689..411549d484e34 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; } /* From 5da35139f4fda7569663a1667b5a745d478a661d Mon Sep 17 00:00:00 2001 From: Marissa Wall Date: Thu, 16 Nov 2017 14:32:40 -0800 Subject: [PATCH 028/296] BACKPORT: USB: core: harden cdc_parse_cdc_header Andrey Konovalov reported a possible out-of-bounds problem for the cdc_parse_cdc_header function. He writes: It looks like cdc_parse_cdc_header() doesn't validate buflen before accessing buffer[1], buffer[2] and so on. The only check present is while (buflen > 0). So fix this issue up by properly validating the buffer length matches what the descriptor says it is. (cherry picked from commit 2e1c42391ff2556387b3cb6308b24f6f65619feb) (The original patch fixed the generic cdc_parser_cdc_header function. That generic function did not exist in 3.10 but there are a couple cdc parsers that suffer from the same underlying problem.) Bug: 69052594 Change-Id: Ib251469de39e51b0ed7c1a1b88873270afccd90f Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Marissa Wall Signed-off-by: anupritaisno1 --- drivers/net/usb/cdc-phonet.c | 4 ++-- drivers/net/usb/cdc_ether.c | 8 +++++++- drivers/net/usb/qmi_wwan.c | 7 ++++++- drivers/usb/class/cdc-acm.c | 5 +++++ drivers/usb/class/cdc-wdm.c | 7 ++++++- 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 7d78669000d70..af49e8fcd7113 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -343,9 +343,9 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) data = intf->altsetting->extra; len = intf->altsetting->extralen; - while (len >= 3) { + while (len > 0) { u8 dlen = data[0]; - if (dlen < 3) + if ((len < dlen) || (dlen < 3 )) return -EINVAL; /* bDescriptorType */ diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index ea0a686a09ebd..1bcb77a9f4df7 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -128,7 +128,13 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) memset(info, 0, sizeof *info); info->control = intf; - while (len > 3) { + while (len > 0) { + + if ((len < buf [0]) || (buf [0] < 3)) { + dev_dbg(&intf->dev, "invalid descriptor buffer length\n"); + goto bad_desc; + } + if (buf [1] != USB_DT_CS_INTERFACE) goto next_desc; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 12aaf1f4f8902..95b616bf48f25 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -235,9 +235,14 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) info->data = intf; /* and a number of CDC descriptors */ - while (len > 3) { + while (len > 0) { struct usb_descriptor_header *h = (void *)buf; + if ((len < buf[0]) || (buf[0] < 3)) { + dev_dbg(&intf->dev, "invalid descriptor buffer length\n"); + goto err; + } + /* ignore any misplaced descriptors */ if (h->bDescriptorType != USB_DT_CS_INTERFACE) goto next_desc; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 802df033e24cd..62e3b48062e3b 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1028,6 +1028,11 @@ static int acm_probe(struct usb_interface *intf, } while (buflen > 0) { + if ((buflen < buffer[0]) || (buffer[0] < 3)) { + dev_err(&intf->dev, "invalid descriptor buffer length\n"); + break; + } + if (buffer[1] != USB_DT_CS_INTERFACE) { dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 07133d0c971b4..e5b24e51946d2 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -841,7 +841,12 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) if (!buffer) goto err; - while (buflen > 2) { + while (buflen > 0) { + if ((buflen < buffer[0]) || (buffer[0] < 3)) { + dev_err(&intf->dev, "invalid descriptor buffer length\n"); + goto err; + } + if (buffer[1] != USB_DT_CS_INTERFACE) { dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; From 5e7f9b458c7bf755e9682a2d640ebc799b346df3 Mon Sep 17 00:00:00 2001 From: Peng Xu Date: Mon, 27 Nov 2017 21:09:51 +0530 Subject: [PATCH 029/296] 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_WOWLAN_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 Bug: 64403015 Signed-off-by: Peng Xu Signed-off-by: Jouni Malinen [rewrite description based on SKB allocation knowledge] Signed-off-by: Johannes Berg 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 Signed-off-by: Srinivas Dasari Signed-off-by: anupritaisno1 --- net/wireless/nl80211.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a6e641809097e..dc9a746b06f91 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -458,6 +458,14 @@ nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, }; +/* policy for packet pattern attributes */ +static const struct nla_policy +nl80211_packet_pattern_policy[MAX_NL80211_WOWLAN_PKTPAT + 1] = { + [NL80211_WOWLAN_PKTPAT_MASK] = { .type = NLA_BINARY, }, + [NL80211_WOWLAN_PKTPAT_PATTERN] = { .type = NLA_BINARY, }, + [NL80211_WOWLAN_PKTPAT_OFFSET] = { .type = NLA_U32 }, +}; + static int nl80211_prepare_wdev_dump(struct sk_buff *skb, struct netlink_callback *cb, struct cfg80211_registered_device **rdev, @@ -8180,7 +8188,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) { nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, - nla_data(pat), nla_len(pat), NULL); + nla_data(pat), nla_len(pat), nl80211_packet_pattern_policy); err = -EINVAL; if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) From c713a996e4df87087003a380b9c22ea53909d8b3 Mon Sep 17 00:00:00 2001 From: Skylar Chang Date: Fri, 11 Aug 2017 15:49:21 -0700 Subject: [PATCH 030/296] 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. Bug: 68992477 Change-Id: Ia9190c60361cb5605b61963309beca3acdeac89d Signed-off-by: Skylar Chang Signed-off-by: anupritaisno1 --- drivers/platform/msm/ipa/rmnet_ipa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/msm/ipa/rmnet_ipa.c b/drivers/platform/msm/ipa/rmnet_ipa.c index 3eb2674f4ba36..658ad89aa3607 100644 --- a/drivers/platform/msm/ipa/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/rmnet_ipa.c @@ -1360,6 +1360,7 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) mutex_lock(&add_mux_channel_lock); /* already got Q6 UL filter rules*/ rc = wwan_add_ul_flt_rule_to_ipa(); + mutex_unlock(&add_mux_channel_lock); egress_set = true; if (rc) IPAWANERR("install UL rules failed\n"); From 8a3bce6409009046b378564187caad97df3beb13 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 28 Nov 2017 18:01:38 -0800 Subject: [PATCH 031/296] 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 Signed-off-by: anupritaisno1 --- 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 ade790b454e99..758688b4bc6b4 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -197,11 +197,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 ac4d76350d1b7..956979787efb7 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 f40acc953ed8a..313c76fbc5e1f 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -70,6 +70,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); From d355c4660504d2921d366c8a2658d48052db1a8a Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Wed, 10 May 2017 09:53:40 +0200 Subject: [PATCH 032/296] UPSTREAM: scsi: sg: don't return bogus Sg_requests If the list search in sg_get_rq_mark() fails to find a valid request, we return a bogus element. This then can later lead to a GPF in sg_remove_scat(). So don't return bogus Sg_requests in sg_get_rq_mark() but NULL in case the list search doesn't find a valid request. Signed-off-by: Johannes Thumshirn Reported-by: Andrey Konovalov Cc: Hannes Reinecke Cc: Christoph Hellwig Cc: Doug Gilbert Reviewed-by: Hannes Reinecke Acked-by: Doug Gilbert Signed-off-by: Martin K. Petersen Signed-off-by: Chenbo Feng (cherry picked from commit 48ae8484e9fc324b4968d33c585e54bc98e44d61) Change-Id: If95d1a8eef3748c9937201e524184b89a5eaaf2e Bug: 75300370 Signed-off-by: anupritaisno1 --- drivers/scsi/sg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 3810c3ff36a2d..aadd3b048f314 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2033,11 +2033,12 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id) if ((1 == resp->done) && (!resp->sg_io_owned) && ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { resp->done = 2; /* guard against other readers */ - break; + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return resp; } } write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + return NULL; } /* always adds to end of list */ From e849719a7b10fef86ed308ac54801ae0b8e08fc0 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 25 Nov 2014 10:01:16 +0100 Subject: [PATCH 033/296] UPSTREAM: kernel: Provide READ_ONCE and ASSIGN_ONCE ACCESS_ONCE does not work reliably on non-scalar types. For example gcc 4.6 and 4.7 might remove the volatile tag for such accesses during the SRA (scalar replacement of aggregates) step https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145) Let's provide READ_ONCE/ASSIGN_ONCE that will do all accesses via scalar types as suggested by Linus Torvalds. Accesses larger than the machines word size cannot be guaranteed to be atomic. These macros will use memcpy and emit a build warning. Signed-off-by: Christian Borntraeger Bug: 28705983 (cherry picked from commit 230fa253df6352af12ad0a16128760b5cb3f92df) Change-Id: I6235247c7b3a5e14093d477417000ed82f7586a3 Signed-off-by: anupritaisno1 --- include/linux/compiler.h | 74 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 6977192bdb595..9df1978e064ce 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -179,6 +179,80 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); # define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__) #endif +#include + +static __always_inline void data_access_exceeds_word_size(void) +#ifdef __compiletime_warning +__compiletime_warning("data access exceeds word size and won't be atomic") +#endif +; + +static __always_inline void data_access_exceeds_word_size(void) +{ +} + +static __always_inline void __read_once_size(volatile void *p, void *res, int size) +{ + switch (size) { + case 1: *(__u8 *)res = *(volatile __u8 *)p; break; + case 2: *(__u16 *)res = *(volatile __u16 *)p; break; + case 4: *(__u32 *)res = *(volatile __u32 *)p; break; +#ifdef CONFIG_64BIT + case 8: *(__u64 *)res = *(volatile __u64 *)p; break; +#endif + default: + barrier(); + __builtin_memcpy((void *)res, (const void *)p, size); + data_access_exceeds_word_size(); + barrier(); + } +} + +static __always_inline void __assign_once_size(volatile void *p, void *res, int size) +{ + switch (size) { + case 1: *(volatile __u8 *)p = *(__u8 *)res; break; + case 2: *(volatile __u16 *)p = *(__u16 *)res; break; + case 4: *(volatile __u32 *)p = *(__u32 *)res; break; +#ifdef CONFIG_64BIT + case 8: *(volatile __u64 *)p = *(__u64 *)res; break; +#endif + default: + barrier(); + __builtin_memcpy((void *)p, (const void *)res, size); + data_access_exceeds_word_size(); + barrier(); + } +} + +/* + * Prevent the compiler from merging or refetching reads or writes. The + * compiler is also forbidden from reordering successive instances of + * READ_ONCE, ASSIGN_ONCE and ACCESS_ONCE (see below), but only when the + * compiler is aware of some particular ordering. One way to make the + * compiler aware of ordering is to put the two invocations of READ_ONCE, + * ASSIGN_ONCE or ACCESS_ONCE() in different C statements. + * + * In contrast to ACCESS_ONCE these two macros will also work on aggregate + * data types like structs or unions. If the size of the accessed data + * type exceeds the word size of the machine (e.g., 32 bits or 64 bits) + * READ_ONCE() and ASSIGN_ONCE() will fall back to memcpy and print a + * compile-time warning. + * + * Their two major use cases are: (1) Mediating communication between + * process-level code and irq/NMI handlers, all running on the same CPU, + * and (2) Ensuring that the compiler does not fold, spindle, or otherwise + * mutilate accesses that either do not require ordering or that interact + * with an explicit memory barrier or atomic instruction that provides the + * required ordering. + */ + +#define READ_ONCE(x) \ + ({ typeof(x) __val; __read_once_size(&x, &__val, sizeof(__val)); __val; }) + +#define ASSIGN_ONCE(val, x) \ + ({ typeof(x) __val; __val = val; __assign_once_size(&x, &__val, sizeof(__val)); __val; }) + #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ From 4e59deade507f81a95fc2aae5165ba914cb9ef57 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 9 Feb 2016 11:15:14 -0800 Subject: [PATCH 034/296] BACKPORT: futex: Remove requirement for lock_page() in get_futex_key() commit 65d8fc777f6dcfee12785c057a6b57f679641c90 upstream. When dealing with key handling for shared futexes, we can drastically reduce the usage/need of the page lock. 1) For anonymous pages, the associated futex object is the mm_struct which does not require the page lock. 2) For inode based, keys, we can check under RCU read lock if the page mapping is still valid and take reference to the inode. This just leaves one rare race that requires the page lock in the slow path when examining the swapcache. Additionally realtime users currently have a problem with the page lock being contended for unbounded periods of time during futex operations. Task A get_futex_key() lock_page() ---> preempted Now any other task trying to lock that page will have to wait until task A gets scheduled back in, which is an unbound time. With this patch, we pretty much have a lockless futex_get_key(). Experiments show that this patch can boost/speedup the hashing of shared futexes with the perf futex benchmarks (which is good for measuring such change) by up to 45% when there are high (> 100) thread counts on a 60 core Westmere. Lower counts are pretty much in the noise range or less than 10%, but mid range can be seen at over 30% overall throughput (hash ops/sec). This makes anon-mem shared futexes much closer to its private counterpart. Signed-off-by: Mel Gorman [ Ported on top of thp refcount rework, changelog, comments, fixes. ] Signed-off-by: Davidlohr Bueso Reviewed-by: Thomas Gleixner Cc: Chris Mason Cc: Darren Hart Cc: Hugh Dickins Cc: Linus Torvalds Cc: Mel Gorman Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: dave@stgolabs.net Link: http://lkml.kernel.org/r/1455045314-8305-3-git-send-email-dave@stgolabs.net Signed-off-by: Ingo Molnar Signed-off-by: Chenbo Feng Signed-off-by: Greg Kroah-Hartman Bug: 74250718 Change-Id: I80e300aa740a553fbbbf84143d6a4165f31c8a90 Signed-off-by: anupritaisno1 # Conflicts: # kernel/futex.c --- kernel/futex.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index f676b873d07d1..191a280d1275e 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -243,6 +243,7 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) unsigned long address = (unsigned long)uaddr; struct mm_struct *mm = current->mm; struct page *page, *page_head; + struct address_space *mapping; int err, ro = 0; /* @@ -320,7 +321,19 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) } #endif - lock_page(page_head); + /* + * The treatment of mapping from this point on is critical. The page + * lock protects many things but in this context the page lock + * stabilizes mapping, prevents inode freeing in the shared + * file-backed region case and guards against movement to swap cache. + * + * Strictly speaking the page lock is not needed in all cases being + * considered here and page lock forces unnecessarily serialization + * From this point on, mapping will be re-verified if necessary and + * page lock will be acquired only if it is unavoidable + */ + + mapping = READ_ONCE(page_head->mapping); /* * If page_head->mapping is NULL, then it cannot be a PageAnon @@ -337,18 +350,31 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) * shmem_writepage move it from filecache to swapcache beneath us: * an unlikely race, but we do need to retry for page_head->mapping. */ - if (!page_head->mapping) { - int shmem_swizzled = PageSwapCache(page_head); + if (unlikely(!mapping)) { + int shmem_swizzled; + + /* + * Page lock is required to identify which special case above + * applies. If this is really a shmem page then the page lock + * will prevent unexpected transitions. + */ + lock_page(page); + shmem_swizzled = PageSwapCache(page) || page->mapping; unlock_page(page_head); put_page(page_head); + if (shmem_swizzled) goto again; + return -EFAULT; } /* * Private mappings are handled in a simple way. * + * If the futex key is stored on an anonymous page, then the associated + * object is the mm which is implicitly pinned by the calling process. + * * NOTE: When userspace waits on a MAP_SHARED mapping, even if * it's a read-only handle, it's expected that futexes attach to * the object not the particular process. @@ -366,16 +392,74 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */ key->private.mm = mm; key->private.address = address; + + get_futex_key_refs(key); /* implies smp_mb(); (B) */ + } else { + struct inode *inode; + + /* + * The associated futex object in this case is the inode and + * the page->mapping must be traversed. Ordinarily this should + * be stabilised under page lock but it's not strictly + * necessary in this case as we just want to pin the inode, not + * update the radix tree or anything like that. + * + * The RCU read lock is taken as the inode is finally freed + * under RCU. If the mapping still matches expectations then the + * mapping->host can be safely accessed as being a valid inode. + */ + rcu_read_lock(); + + if (READ_ONCE(page_head->mapping) != mapping) { + rcu_read_unlock(); + put_page(page_head); + + goto again; + } + + inode = READ_ONCE(mapping->host); + if (!inode) { + rcu_read_unlock(); + put_page(page_head); + + goto again; + } + + /* + * Take a reference unless it is about to be freed. Previously + * this reference was taken by ihold under the page lock + * pinning the inode in place so i_lock was unnecessary. The + * only way for this check to fail is if the inode was + * truncated in parallel so warn for now if this happens. + * + * We are not calling into get_futex_key_refs() in file-backed + * cases, therefore a successful atomic_inc return below will + * guarantee that get_futex_key() will still imply smp_mb(); (B). + */ + if (WARN_ON_ONCE(!atomic_inc_not_zero(&inode->i_count))) { + rcu_read_unlock(); + put_page(page_head); + + goto again; + } + + /* Should be impossible but lets be paranoid for now */ + if (WARN_ON_ONCE(inode->i_mapping != mapping)) { + err = -EFAULT; + rcu_read_unlock(); + iput(inode); + + goto out; + } + key->both.offset |= FUT_OFF_INODE; /* inode-based key */ - key->shared.inode = page_head->mapping->host; + key->shared.inode = inode; key->shared.pgoff = basepage_index(page); + rcu_read_unlock(); } - get_futex_key_refs(key); - out: - unlock_page(page_head); put_page(page_head); return err; } From d2e486aa5719aa89a14267b3c4b6252a7676ca49 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 12 May 2018 21:03:47 +0100 Subject: [PATCH 035/296] op2: enforce dm-verity on target Test: build with full verity, boot device All tests have passed successfully Signed-off-by: anupritaisno1 --- arch/arm/boot/dts/qcom/msm8994.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8994.dtsi b/arch/arm/boot/dts/qcom/msm8994.dtsi index 93a1cc7a60038..09a274626dcfe 100644 --- a/arch/arm/boot/dts/qcom/msm8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm8994.dtsi @@ -37,7 +37,7 @@ dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/system"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; - fsmgr_flags = "wait"; + fsmgr_flags = "wait,verify"; status = "ok"; }; }; From e48ac17107acadfb1a5016d090cca894c4ff2042 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 3 Oct 2017 08:25:07 -0400 Subject: [PATCH 036/296] camera_v2: isp: Replace monotonic timestamp with boot_time BOOT_TIME timestamp takes into consideration of sleep time. Bug: 22214409 Change-Id: I927aa07fe81a7e6e644eca18b75fae077f52c5f7 Signed-off-by: Shuzhen Wang Signed-off-by: anupritaisno1 --- drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 d1315068515c3..306d6db7471f7 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 @@ -334,9 +334,9 @@ int msm_isp_get_clk_info(struct vfe_device *vfe_dev, static inline void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp) { struct timespec ts; - ktime_get_ts(&ts); - time_stamp->buf_time.tv_sec = ts.tv_sec; - time_stamp->buf_time.tv_usec = ts.tv_nsec/1000; + get_monotonic_boottime(&ts); + time_stamp->buf_time.tv_sec = ts.tv_sec; + time_stamp->buf_time.tv_usec = ts.tv_nsec/1000; do_gettimeofday(&(time_stamp->event_time)); } From 8a6727f22f134776d5cc3b9c96367813ba21f074 Mon Sep 17 00:00:00 2001 From: annamraj Date: Wed, 19 Jul 2017 16:52:55 +0530 Subject: [PATCH 037/296] msm: camera: Make use of mutex lock to avoid race condition Make use of mutex lock to access IOCTL so that two threads can avoid race condition. Bug: 64728950 Bug: 65468993 Change-Id: I00db78a42c86eef8a157b5b3547e4ca0006b0853 Signed-off-by: annamraj Signed-off-by: anupritaisno1 --- drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c index c19b0d4b61c22..b4616f60dce12 100644 --- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c @@ -991,15 +991,19 @@ static int msm_fd_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) a->value = ctx->format.size->work_size; break; case V4L2_CID_FD_WORK_MEMORY_FD: + mutex_lock(&ctx->fd_device->recovery_lock); if (ctx->work_buf.handle) msm_fd_hw_unmap_buffer(&ctx->work_buf); if (a->value >= 0) { ret = msm_fd_hw_map_buffer(&ctx->mem_pool, a->value, &ctx->work_buf); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&ctx->fd_device->recovery_lock); return ret; + } } + mutex_unlock(&ctx->fd_device->recovery_lock); break; default: return -EINVAL; From 2dc9f0ab440460ee851d96f22b964aa9f775f982 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Wed, 6 Dec 2017 22:20:13 +0100 Subject: [PATCH 038/296] ANDROID: binder: fix OOB write in __binder_update_page_range commit e12f1f1c65a9 ("binder: always allocate/map first BINDER_MIN_ALLOC pages") introduced a vulnerability where the alloc->pages array can be accessed beyond the allocated elements. For this to occur, the passed-in VMA range must be < BINDER_MIN_ALLOC pages (which cannot happen for normal use cases). BINDER_MIN_ALLOC is set to 6 pages, so mmap'ing 1 page results in OOB accesses. The fix is to return an error if binder_mmap is called with a VMA size less than BINDER_MIN_ALLOC. Bug: 64216036 Test: verified fixed with submitter's test program Change-Id: I4b6dbf3f466fea46e587d6a33f724ee4fe7f1368 Signed-off-by: Todd Kjos Signed-off-by: anupritaisno1 --- drivers/android/binder.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 921c0e93166aa..6a19f88bc6db0 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3519,6 +3519,11 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } } #endif + if (vma->vm_end - vma->vm_start < BINDER_MIN_ALLOC) { + ret = -EINVAL; + failure_string = "VMA size < BINDER_MIN_ALLOC"; + goto err_vma_too_small; + } proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); if (proc->pages == NULL) { ret = -ENOMEM; @@ -3558,6 +3563,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) kfree(proc->pages); proc->pages = NULL; err_alloc_pages_failed: +err_vma_too_small: mutex_lock(&proc->context->binder_mmap_lock); vfree(proc->buffer); proc->buffer = NULL; From 7ad2d167964231ecde15846fbe4bdcf8a2851f4a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jul 2013 15:02:11 -0700 Subject: [PATCH 039/296] UPSTREAM: include/linux/mm.h: add PAGE_ALIGNED() helper To test whether an address is aligned to PAGE_SIZE. Cc: HATAYAMA Daisuke Cc: "Eric W. Biederman" , Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 0fa73b86ef0797ca4fde5334117ca0b330f08030) Bug: 36007193 Change-Id: I7e912bb0dbd8c9737fb13c5b48acb54ee39dd5fc Signed-off-by: anupritaisno1 --- include/linux/mm.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index e4bef3028af0a..ce8ec01d02673 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -70,6 +70,9 @@ extern unsigned long sysctl_admin_reserve_kbytes; /* to align the pointer to the (next) page boundary */ #define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) +/* test whether an address (unsigned long or pointer) is aligned to PAGE_SIZE */ +#define PAGE_ALIGNED(addr) IS_ALIGNED((unsigned long)addr, PAGE_SIZE) + /* * Linux kernel virtual memory manager primitives. * The idea being to have a "virtual" mm in the same way From 551c335929da1616b2c233e980138062ec9f866a Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Fri, 30 Jun 2017 10:22:23 -0700 Subject: [PATCH 040/296] android: binder: Refactor prev and next buffer into a helper function Bug: 36007193 Change-Id: I422dce84afde3d2138a6d976593b109a9cc49003 Signed-off-by: Sherry Yang Signed-off-by: anupritaisno1 --- drivers/android/binder.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 6a19f88bc6db0..b46b025af3f93 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -558,13 +558,22 @@ static void binder_set_nice(long nice) binder_user_error("%d RLIMIT_NICE not set\n", current->pid); } +static struct binder_buffer *binder_buffer_next(struct binder_buffer *buffer) +{ + return list_entry(buffer->entry.next, struct binder_buffer, entry); +} + +static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer) +{ + return list_entry(buffer->entry.prev, struct binder_buffer, entry); +} + static size_t binder_buffer_size(struct binder_proc *proc, struct binder_buffer *buffer) { if (list_is_last(&buffer->entry, &proc->buffers)) return proc->buffer + proc->buffer_size - (void *)buffer->data; - return (size_t)list_entry(buffer->entry.next, - struct binder_buffer, entry) - (size_t)buffer->data; + return (size_t)binder_buffer_next(buffer) - (size_t)buffer->data; } static void binder_insert_free_buffer(struct binder_proc *proc, @@ -893,7 +902,7 @@ static void binder_delete_free_buffer(struct binder_proc *proc, int free_page_start = 1; BUG_ON(proc->buffers.next == &buffer->entry); - prev = list_entry(buffer->entry.prev, struct binder_buffer, entry); + prev = binder_buffer_prev(buffer); BUG_ON(!prev->free); if (buffer_end_page(prev) == buffer_start_page(buffer)) { free_page_start = 0; @@ -905,8 +914,7 @@ static void binder_delete_free_buffer(struct binder_proc *proc, } if (!list_is_last(&buffer->entry, &proc->buffers)) { - next = list_entry(buffer->entry.next, - struct binder_buffer, entry); + next = binder_buffer_next(buffer); if (buffer_start_page(next) == buffer_end_page(buffer)) { free_page_end = 0; if (buffer_start_page(next) == @@ -966,8 +974,7 @@ static void binder_free_buf(struct binder_proc *proc, rb_erase(&buffer->rb_node, &proc->allocated_buffers); buffer->free = 1; if (!list_is_last(&buffer->entry, &proc->buffers)) { - struct binder_buffer *next = list_entry(buffer->entry.next, - struct binder_buffer, entry); + struct binder_buffer *next = binder_buffer_next(buffer); if (next->free) { rb_erase(&next->rb_node, &proc->free_buffers); @@ -975,8 +982,7 @@ static void binder_free_buf(struct binder_proc *proc, } } if (proc->buffers.next != &buffer->entry) { - struct binder_buffer *prev = list_entry(buffer->entry.prev, - struct binder_buffer, entry); + struct binder_buffer *prev = binder_buffer_prev(buffer); if (prev->free) { binder_delete_free_buffer(proc, buffer); From 410bfd70e244d8f7d648d80f83920166459ae5ef Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Thu, 6 Jul 2017 22:51:19 -0700 Subject: [PATCH 041/296] android: binder: Move buffer out of area shared with user space Binder driver allocates buffer meta data in a region that is mapped in user space. These meta data contain pointers in the kernel. This patch allocates buffer meta data on the kernel heap that is not mapped in user space, and uses a pointer to refer to the data mapped. Also move alloc->buffers initialization from mmap to init since it's now used even when mmap failed or was not called. Bug: 36007193 Change-Id: Id5136048bdb7b796f59de066de7ea7df410498f5 Signed-off-by: Sherry Yang Signed-off-by: anupritaisno1 --- drivers/android/binder.c | 139 ++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 53 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index b46b025af3f93..5542a6121c6e1 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -322,7 +322,7 @@ struct binder_buffer { size_t data_size; size_t offsets_size; size_t extra_buffers_size; - uint8_t data[0]; + void *data; }; enum binder_deferred_state { @@ -572,8 +572,9 @@ static size_t binder_buffer_size(struct binder_proc *proc, struct binder_buffer *buffer) { if (list_is_last(&buffer->entry, &proc->buffers)) - return proc->buffer + proc->buffer_size - (void *)buffer->data; - return (size_t)binder_buffer_next(buffer) - (size_t)buffer->data; + return (u8 *)proc->buffer + + proc->buffer_size - (u8 *)buffer->data; + return (u8 *)binder_buffer_next(buffer)->data - (u8 *)buffer->data; } static void binder_insert_free_buffer(struct binder_proc *proc, @@ -623,9 +624,9 @@ static void binder_insert_allocated_buffer(struct binder_proc *proc, buffer = rb_entry(parent, struct binder_buffer, rb_node); BUG_ON(buffer->free); - if (new_buffer < buffer) + if (new_buffer->data < buffer->data) p = &parent->rb_left; - else if (new_buffer > buffer) + else if (new_buffer->data > buffer->data) p = &parent->rb_right; else BUG(); @@ -639,18 +640,17 @@ static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc, { struct rb_node *n = proc->allocated_buffers.rb_node; struct binder_buffer *buffer; - struct binder_buffer *kern_ptr; + void *kern_ptr; - kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset - - offsetof(struct binder_buffer, data)); + kern_ptr = (void *)(user_ptr - proc->user_buffer_offset); while (n) { buffer = rb_entry(n, struct binder_buffer, rb_node); BUG_ON(buffer->free); - if (kern_ptr < buffer) + if (kern_ptr < buffer->data) n = n->rb_left; - else if (kern_ptr > buffer) + else if (kern_ptr > buffer->data) n = n->rb_right; else return buffer; @@ -812,6 +812,9 @@ static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, return NULL; } + /* Pad 0-size buffers so they get assigned unique addresses */ + size = max(size, sizeof(void *)); + while (n) { buffer = rb_entry(n, struct binder_buffer, rb_node); BUG_ON(!buffer->free); @@ -843,30 +846,32 @@ static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, has_page_addr = (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); - if (n == NULL) { - if (size + sizeof(struct binder_buffer) + 4 >= buffer_size) - buffer_size = size; /* no room for other buffers */ - else - buffer_size = size + sizeof(struct binder_buffer); - } + WARN_ON(n && buffer_size != size); end_page_addr = - (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size); + (void *)PAGE_ALIGN((uintptr_t)buffer->data + size); if (end_page_addr > has_page_addr) end_page_addr = has_page_addr; if (binder_update_page_range(proc, 1, (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL)) return NULL; - - rb_erase(best_fit, &proc->free_buffers); - buffer->free = 0; - binder_insert_allocated_buffer(proc, buffer); if (buffer_size != size) { - struct binder_buffer *new_buffer = (void *)buffer->data + size; + struct binder_buffer *new_buffer; + new_buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!new_buffer) { + pr_err("%s: %d failed to alloc new buffer struct\n", + __func__, proc->pid); + goto err_alloc_buf_struct_failed; + } + new_buffer->data = (u8 *)buffer->data + size; list_add(&new_buffer->entry, &buffer->entry); new_buffer->free = 1; binder_insert_free_buffer(proc, new_buffer); } + + rb_erase(best_fit, &proc->free_buffers); + buffer->free = 0; + binder_insert_allocated_buffer(proc, buffer); binder_debug(BINDER_DEBUG_BUFFER_ALLOC, "%d: binder_alloc_buf size %zd got %pK\n", proc->pid, size, buffer); @@ -882,60 +887,70 @@ static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, } return buffer; + +err_alloc_buf_struct_failed: + binder_update_page_range(proc, 0, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), + end_page_addr, NULL); + return NULL; } static void *buffer_start_page(struct binder_buffer *buffer) { - return (void *)((uintptr_t)buffer & PAGE_MASK); + return (void *)((uintptr_t)buffer->data & PAGE_MASK); } -static void *buffer_end_page(struct binder_buffer *buffer) +static void *prev_buffer_end_page(struct binder_buffer *buffer) { - return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK); + return (void *)(((uintptr_t)(buffer->data) - 1) & PAGE_MASK); } static void binder_delete_free_buffer(struct binder_proc *proc, struct binder_buffer *buffer) { struct binder_buffer *prev, *next = NULL; - int free_page_end = 1; - int free_page_start = 1; + bool to_free = true; BUG_ON(proc->buffers.next == &buffer->entry); prev = binder_buffer_prev(buffer); BUG_ON(!prev->free); - if (buffer_end_page(prev) == buffer_start_page(buffer)) { - free_page_start = 0; - if (buffer_end_page(prev) == buffer_end_page(buffer)) - free_page_end = 0; + if (prev_buffer_end_page(prev) == buffer_start_page(buffer)) { + to_free = false; binder_debug(BINDER_DEBUG_BUFFER_ALLOC, "%d: merge free, buffer %pK share page with %pK\n", - proc->pid, buffer, prev); + proc->pid, buffer->data, prev->data); } if (!list_is_last(&buffer->entry, &proc->buffers)) { next = binder_buffer_next(buffer); - if (buffer_start_page(next) == buffer_end_page(buffer)) { - free_page_end = 0; - if (buffer_start_page(next) == - buffer_start_page(buffer)) - free_page_start = 0; + if (buffer_start_page(next) == buffer_start_page(buffer)) { + to_free = false; binder_debug(BINDER_DEBUG_BUFFER_ALLOC, "%d: merge free, buffer %pK share page with %pK\n", - proc->pid, buffer, prev); + proc->pid, + buffer->data, + next->data); } } - list_del(&buffer->entry); - if (free_page_start || free_page_end) { + + if (PAGE_ALIGNED(buffer->data)) { + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer start %pK is page aligned\n", + proc->pid, buffer->data); + to_free = false; + } + + if (to_free) { binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n", - proc->pid, buffer, free_page_start ? "" : " end", - free_page_end ? "" : " start", prev, next); - binder_update_page_range(proc, 0, free_page_start ? - buffer_start_page(buffer) : buffer_end_page(buffer), - (free_page_end ? buffer_end_page(buffer) : - buffer_start_page(buffer)) + PAGE_SIZE, NULL); + "%d: merge free, buffer %pK do not share page with %pK or %pK\n", + proc->pid, buffer->data, + prev->data, next->data); + binder_update_page_range(proc, 0, buffer_start_page(buffer), + buffer_start_page(buffer) + PAGE_SIZE, + NULL); } + list_del(&buffer->entry); + kfree(buffer); } static void binder_free_buf(struct binder_proc *proc, @@ -956,8 +971,8 @@ static void binder_free_buf(struct binder_proc *proc, BUG_ON(buffer->free); BUG_ON(size > buffer_size); BUG_ON(buffer->transaction != NULL); - BUG_ON((void *)buffer < proc->buffer); - BUG_ON((void *)buffer > proc->buffer + proc->buffer_size); + BUG_ON(buffer->data < proc->buffer); + BUG_ON(buffer->data > proc->buffer + proc->buffer_size); if (buffer->async_transaction) { proc->free_async_space += size + sizeof(struct binder_buffer); @@ -3541,6 +3556,13 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + failure_string = "alloc buffer struct"; + goto err_alloc_buf_struct_failed; + } + /* binder_update_page_range assumes preemption is disabled */ preempt_disable(); ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma); @@ -3550,8 +3572,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) failure_string = "alloc small buf"; goto err_alloc_small_buf_failed; } - buffer = proc->buffer; - INIT_LIST_HEAD(&proc->buffers); + buffer->data = proc->buffer; list_add(&buffer->entry, &proc->buffers); buffer->free = 1; binder_insert_free_buffer(proc, buffer); @@ -3566,6 +3587,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) return 0; err_alloc_small_buf_failed: + kfree(buffer); +err_alloc_buf_struct_failed: kfree(proc->pages); proc->pages = NULL; err_alloc_pages_failed: @@ -3598,6 +3621,7 @@ static int binder_open(struct inode *nodp, struct file *filp) INIT_LIST_HEAD(&proc->todo); init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); + INIT_LIST_HEAD(&proc->buffers); binder_dev = container_of(filp->private_data, struct binder_device, miscdev); proc->context = &binder_dev->context; @@ -3722,6 +3746,7 @@ static void binder_deferred_release(struct binder_proc *proc) struct binder_transaction *t; struct binder_context *context = proc->context; struct rb_node *n; + struct binder_buffer *buffer; int threads, nodes, incoming_refs, outgoing_refs, buffers, active_transactions, page_count; @@ -3774,8 +3799,6 @@ static void binder_deferred_release(struct binder_proc *proc) buffers = 0; while ((n = rb_first(&proc->allocated_buffers))) { - struct binder_buffer *buffer; - buffer = rb_entry(n, struct binder_buffer, rb_node); t = buffer->transaction; @@ -3793,6 +3816,16 @@ static void binder_deferred_release(struct binder_proc *proc) binder_stats_deleted(BINDER_STAT_PROC); + while (!list_empty(&proc->buffers)) { + buffer = list_first_entry(&proc->buffers, + struct binder_buffer, entry); + WARN_ON(!buffer->free); + + list_del(&buffer->entry); + WARN_ON_ONCE(!list_empty(&proc->buffers)); + kfree(buffer); + } + page_count = 0; if (proc->pages) { int i; From a8cd26a0df4068b65226ac906725c9bdfb55dbb1 Mon Sep 17 00:00:00 2001 From: Steve Pfetsch Date: Fri, 3 Nov 2017 16:26:00 -0700 Subject: [PATCH 042/296] input: synaptics: restrict sysfs node write permissions Bug: 62800865 Change-Id: I83428f32afc77050580d1f1b3f69938cf90f9256 Signed-off-by: Steve Pfetsch Signed-off-by: anupritaisno1 --- drivers/input/touchscreen/synaptics_rmi_dev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/synaptics_rmi_dev.c b/drivers/input/touchscreen/synaptics_rmi_dev.c index 64f051e70e6af..acf201e3c142a 100644 --- a/drivers/input/touchscreen/synaptics_rmi_dev.c +++ b/drivers/input/touchscreen/synaptics_rmi_dev.c @@ -73,19 +73,19 @@ struct rmidev_data { }; static struct device_attribute attrs[] = { - __ATTR(open, S_IWUSR | S_IWGRP, + __ATTR(open, S_IWUSR, NULL, rmidev_sysfs_open_store), - __ATTR(release, S_IWUSR | S_IWGRP, + __ATTR(release, S_IWUSR, NULL, rmidev_sysfs_release_store), - __ATTR(address, S_IWUSR | S_IWGRP, + __ATTR(address, S_IWUSR, NULL, rmidev_sysfs_address_store), - __ATTR(length, S_IWUSR | S_IWGRP, + __ATTR(length, S_IWUSR, NULL, rmidev_sysfs_length_store), - __ATTR(data, (S_IWUSR | S_IWGRP), + __ATTR(data, S_IWUSR, rmidev_sysfs_data_show, rmidev_sysfs_data_store), }; From ba0c4f0dc7198518ce3eae4986b5ba797cb73c05 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 12 Dec 2017 14:25:13 -0500 Subject: [PATCH 043/296] USB: core: prevent malicious bNumInterfaces overflow 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. Change-Id: I33579781fb68163dd76a253501caf44ed9e6d895 Signed-off-by: Alan Stern Reported-by: Andrey Konovalov CC: Signed-off-by: Greg Kroah-Hartman Signed-off-by: anupritaisno1 --- 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 a4fa0a35d24b4..e6e5cb83b8f92 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -500,6 +500,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) { @@ -513,7 +516,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", From 092040c9ef302e387936e5938ac0c5ee54da111d Mon Sep 17 00:00:00 2001 From: Srinivas Girigowda Date: Tue, 5 Dec 2017 15:29:05 -0800 Subject: [PATCH 044/296] qcacld-2.0: Fix potential buffer overwrite in wma_roam_synch_event_handler In the function wma_roam_synch_event_handler, vdev_id is received from the fw and is used to access member of the array wma->interfaces without validating the max of the vdev_id received from the fw Add check to make sure vdev_id is less than max_bssid before using it Change-Id: I3b940e183ab66680891cb7351af4537b50afce1d CRs-Fixed: 2147083 Bug: 68992434 Signed-off-by: Srinivas Girigowda Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/SERVICES/WMA/wma.c | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 455df22a8810d..0e47e29c65172 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -6594,6 +6594,7 @@ static int wma_roam_synch_event_handler(void *handle, u_int8_t *event, u_int32_t wmi_key_material *key = NULL; int size=0; tSirRoamOffloadSynchInd *pRoamOffloadSynchInd; + uint32_t roam_synch_data_len; WMA_LOGD("LFR3:%s", __func__); if (!event) { @@ -6613,35 +6614,56 @@ static int wma_roam_synch_event_handler(void *handle, u_int8_t *event, u_int32_t return -EINVAL; } + if (synch_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, synch_event->vdev_id); + return -EINVAL; + } + if(wma->interfaces[synch_event->vdev_id].roam_synch_in_progress == VOS_TRUE) { WMA_LOGE("%s: Ignoring RSI since one is already in progress", __func__); return -EINVAL; } + + WMA_LOGD("synch payload: LEN bcn:%d, req:%d, rsp:%d", + synch_event->bcn_probe_rsp_len, + synch_event->reassoc_req_len, + synch_event->reassoc_rsp_len); + + if (synch_event->bcn_probe_rsp_len > WMA_SVC_MSG_MAX_SIZE) + return -EINVAL; + if (synch_event->reassoc_rsp_len > + (WMA_SVC_MSG_MAX_SIZE - synch_event->bcn_probe_rsp_len)) + return -EINVAL; + if (synch_event->reassoc_req_len > + WMA_SVC_MSG_MAX_SIZE - (synch_event->bcn_probe_rsp_len + + synch_event->reassoc_rsp_len)) + return -EINVAL; + + roam_synch_data_len = synch_event->bcn_probe_rsp_len + + synch_event->reassoc_rsp_len + + synch_event->reassoc_req_len; /* - * All below length fields are unsigned and hence positive numbers. - * Maximum number during the addition would be (3 * MAX_LIMIT(UINT32) + - * few fixed fields). + * Below is the check for the entire size of the message received from' + * the firmware. */ - if ((sizeof(*synch_event) + synch_event->bcn_probe_rsp_len + - synch_event->reassoc_rsp_len + - sizeof(wmi_channel) + sizeof(wmi_key_material) + - sizeof(uint32_t)) > WMA_SVC_MSG_MAX_SIZE) { - WMA_LOGE("excess synch payload: LEN bcn:%d, rsp:%d", - synch_event->bcn_probe_rsp_len, - synch_event->reassoc_rsp_len); - VOS_ASSERT(0); + if (roam_synch_data_len > WMA_SVC_MSG_MAX_SIZE - + (sizeof(*synch_event) + sizeof(wmi_channel) + + sizeof(wmi_key_material) + sizeof(uint32_t))) return -EINVAL; - } + + if (sizeof(tSirRoamOffloadSynchInd) > + (WMA_SVC_MSG_MAX_SIZE - roam_synch_data_len)) + return -EINVAL; + roam_synch_data_len += sizeof(tSirRoamOffloadSynchInd); adf_os_spin_lock_bh(&wma->roam_synch_lock); wma->interfaces[synch_event->vdev_id].roam_synch_in_progress = VOS_TRUE; adf_os_spin_unlock_bh(&wma->roam_synch_lock); - len = sizeof(tSirRoamOffloadSynchInd) + - synch_event->bcn_probe_rsp_len + - synch_event->reassoc_rsp_len; - pRoamOffloadSynchInd = (tSirRoamOffloadSynchInd *)vos_mem_malloc(len); + pRoamOffloadSynchInd = + (tSirRoamOffloadSynchInd *)vos_mem_malloc(roam_synch_data_len); if (!pRoamOffloadSynchInd) { WMA_LOGE("%s: failed to allocate memory for roam_synch_event", __func__); return -ENOMEM; From 33145d32c14171ca09f2512030baf9cb11711a30 Mon Sep 17 00:00:00 2001 From: Dennis Cagle Date: Mon, 4 Dec 2017 16:42:52 -0800 Subject: [PATCH 045/296] qcacld-2.0: Fix int overflow in wma_unified_link_radio_stats_event_handler Check for the validity of the number of channels passed in the radio stats event received from firmware to ensure an integer overflow does not happen. Change-Id: Idf5738a40139aafad4de422965dc4ff3d0e53a32 CRs-Fixed: 2149958 Bug: 68992439 Signed-off-by: Dennis Cagle Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 0e47e29c65172..0d634a767da18 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -5018,8 +5018,23 @@ static int wma_unified_link_radio_stats_event_handler(void *handle, return -EINVAL; } + if (radio_stats->num_channels > + NUM_2_4GHZ_CHANNELS + NUM_5GHZ_CHANNELS) { + WMA_LOGE("%s: Too many channels %d", + __func__, radio_stats->num_channels); + return -EINVAL; + } + radio_stats_size = sizeof(tSirWifiRadioStat); chan_stats_size = sizeof(tSirWifiChannelStats); + + if (fixed_param->num_radio > + (UINT_MAX - sizeof(*link_stats_results)) / radio_stats_size) { + WMA_LOGE("excess num_radio %d is leading to int overflow", + fixed_param->num_radio); + return -EINVAL; + } + link_stats_results_size = sizeof(*link_stats_results) + radio_stats_size + (radio_stats->num_channels * chan_stats_size); From 8d31308cf9ab684733e3c44264a5d56d934a71e0 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Tue, 28 Nov 2017 18:22:11 -0800 Subject: [PATCH 046/296] 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 Signed-off-by: anupritaisno1 --- 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 3840ff8a49163..cdf5bfe42567b 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", @@ -2017,6 +2015,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); @@ -2046,6 +2045,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); @@ -2250,10 +2250,12 @@ static int ctrl_cmd_tag(const char *input) full_tag = combine_atag_with_uid(acct_tag, uid); 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; } @@ -2280,9 +2282,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 @@ -2293,7 +2300,6 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); - spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); /* @@ -2311,11 +2317,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", @@ -2324,10 +2330,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); From eac9494d0785fef9ccfff16f6266786b0bc94473 Mon Sep 17 00:00:00 2001 From: "choongryeol.lee" Date: Wed, 26 Aug 2015 17:04:30 -0700 Subject: [PATCH 047/296] i2c-msm-v2: use subsys_initcall to avoid probe fail Since clock controller is registered by arch_initcall, i2c controller probe could be failed by clk_get fail. So, use subsys_initcall rather than arch_initcall. Change-Id: Ie81bf23f431d0e28ff9bd59ce1b0008aaaa43cdc Signed-off-by: choongryeol.lee Signed-off-by: anupritaisno1 --- drivers/i2c/busses/i2c-msm-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index a5dd336bb2b5e..90c078a471262 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -2973,7 +2973,7 @@ static int i2c_msm_init(void) { return platform_driver_register(&i2c_msm_driver); } -arch_initcall(i2c_msm_init); +subsys_initcall(i2c_msm_init); static void i2c_msm_exit(void) { From e30049683767d9d8669146af137d23ba08bbff64 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 19 Mar 2018 15:49:54 +0530 Subject: [PATCH 048/296] ANDROID: sdcardfs: Fix sdcardfs to stop creating cases-sensitive duplicate entries. sdcardfs_name_match gets a 'name' argument from the underlying FS. This need not be null terminated string. So in sdcardfs_name_match -> qstr_case_eq -> we should use str_n_case_eq. This happens because few of the entries in lower level FS may not be NULL terminated and may have some garbage characters passed while doing sdcardfs_name_match. For e.g. # dmesg |grep Download [ 103.646386] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 104.021340] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 105.196864] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 109.113521] sdcardfs_name_match: q1->name=logs, q1->len=4, q2->name=Download\x17\x80\x03, q2->len=8 Now when we try to create a directory with different case for a such files. SDCARDFS creates a entry if it could not find the underlying entry in it's dcache. To reproduce:- 1. bootup the device wait for some time after sdcardfs mounting to complete. 2. cd /storage/emulated/0 3. echo 3 > /proc/sys/vm/drop_caches 4. mkdir download We now start seeing two entries with name. Download & download. Change-Id: I976d92a220a607dd8cdb96c01c2041c5c2bc3326 Signed-off-by: Ritesh Harjani bug: 75987238 Signed-off-by: anupritaisno1 --- fs/sdcardfs/sdcardfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 641b1ec59e62b..2c510e4bee57b 100755 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -669,7 +669,7 @@ static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { - return q1->len == q2->len && str_case_eq(q1->name, q2->name); + return q1->len == q2->len && str_n_case_eq(q1->name, q2->name, q2->len); } #define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1) From 519985d6dd53dbb538879c29762f7cb3ed61a8b3 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 19 Mar 2018 16:03:09 +0530 Subject: [PATCH 049/296] ANDROID: fuse: Add null terminator to path in canonical path to avoid issue page allocated in fuse_dentry_canonical_path to be handled in fuse_dev_do_write is allocated using __get_free_pages(GFP_KERNEL). This may not return a page with data filled with 0. Now this page may not have a null terminator at all. If this happens and userspace fuse daemon screws up by passing a string to kernel which is not NULL terminated (or did not fill anything), then inside fuse driver in kernel when we try to do strlen(fuse_dev_write->kern_path->getname_kernel) on that page data -> it may give us issue with kernel paging request. Unable to handle kernel paging request at virtual address ------------[ cut here ]------------ <..> PC is at strlen+0x10/0x90 LR is at getname_kernel+0x2c/0xf4 <..> strlen+0x10/0x90 kern_path+0x28/0x4c fuse_dev_do_write+0x5b8/0x694 fuse_dev_write+0x74/0x94 do_iter_readv_writev+0x80/0xb8 do_readv_writev+0xec/0x1cc vfs_writev+0x54/0x64 SyS_writev+0x64/0xe4 el0_svc_naked+0x24/0x28 To avoid this we should ensure in case of FUSE_CANONICAL_PATH, the page is null terminated. Change-Id: I33ca7cc76b4472eaa982c67bb20685df451121f5 Signed-off-by: Ritesh Harjani Bug: 75984715 [Daniel - small edit, using args size ] Signed-off-by: Daniel Rosenberg Signed-off-by: anupritaisno1 --- fs/fuse/dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 92d569a638667..0f6b1fca96ca7 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1877,8 +1877,10 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, err = copy_out_args(cs, &req->out, nbytes); if (req->in.h.opcode == FUSE_CANONICAL_PATH) { - req->out.h.error = kern_path((char *)req->out.args[0].value, 0, - req->canonical_path); + char *path = (char *)req->out.args[0].value; + + path[req->out.args[0].size - 1] = 0; + req->out.h.error = kern_path(path, 0, req->canonical_path); } fuse_copy_finish(cs); From 9641713b0bd384de37b2cc636ee164b70239b53e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 16:19:10 -0700 Subject: [PATCH 050/296] ANDROID: sdcardfs: Check for private data earlier When an sdcardfs dentry is destroyed, it may not yet have its fsdata initialized. It must be checked before we try to access the paths in its private data. Additionally, when cleaning up the superblock after a failure, we don't have our sb private data, so check for that case. Bug: 77923821 Change-Id: I89caf6e121ed86480b42024664453fe0031bbcf3 Signed-off-by: Daniel Rosenberg Signed-off-by: anupritaisno1 --- fs/sdcardfs/dentry.c | 2 ++ fs/sdcardfs/lookup.c | 2 -- fs/sdcardfs/main.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index c8f3bcbfb183b..8a35955534ab3 100755 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -131,6 +131,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) static void sdcardfs_d_release(struct dentry *dentry) { + if (!dentry || !dentry->d_fsdata) + return; /* release and reset the lower paths */ if (has_graft_path(dentry)) sdcardfs_put_reset_orig_path(dentry); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index ace852390d2a3..3395371c72cae 100755 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -41,8 +41,6 @@ void sdcardfs_destroy_dentry_cache(void) void free_dentry_private_data(struct dentry *dentry) { - if (!dentry || !dentry->d_fsdata) - return; kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata); dentry->d_fsdata = NULL; } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 512d8d0e57a4e..33be8937fefba 100755 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -429,7 +429,7 @@ void sdcardfs_kill_sb(struct super_block *sb) { struct sdcardfs_sb_info *sbi; - if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { + if (sb->s_magic == SDCARDFS_SUPER_MAGIC && sb->s_fs_info) { sbi = SDCARDFS_SB(sb); mutex_lock(&sdcardfs_super_list_lock); list_del(&sbi->list); From f1b49f93173f793109be68dd60b6ca4492d34ea2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 16:24:51 -0700 Subject: [PATCH 051/296] ANDROID: sdcardfs: d_make_root calls iput d_make_root will call iput on failure, so we shouldn't try to do that ourselves. Signed-off-by: Daniel Rosenberg Bug: 77923821 Change-Id: I1abb4afb0f894ab917b7c6be8c833676f436beb7 Signed-off-by: anupritaisno1 --- fs/sdcardfs/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 33be8937fefba..8527580707723 100755 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -323,7 +323,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; - goto out_iput; + goto out_sput; } d_set_d_op(sb->s_root, &sdcardfs_ci_dops); @@ -368,8 +368,6 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); -out_iput: - iput(inode); out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); From 5ae9bd80210e7e876c41a48b707a6e08cf46b379 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 18:39:43 -0700 Subject: [PATCH 052/296] ANDROID: sdcardfs: Set s_root to NULL after putting Signed-off-by: Daniel Rosenberg Bug: 77923821 Change-Id: I1705bfd146009561d2d1da5f0e6a342ec6932a1c Signed-off-by: anupritaisno1 --- fs/sdcardfs/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 8527580707723..b60f5edfa834d 100755 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -368,6 +368,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); + sb->s_root = NULL; out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); From 1733d6e12c08c7659ffd901158409f350ab458d5 Mon Sep 17 00:00:00 2001 From: Scott Mertz Date: Mon, 16 May 2016 16:01:52 -0700 Subject: [PATCH 053/296] video: mdss: remove erroneous mutex_unlock Noticed the following warning: WARNING: at ../../../../../../kernel/oneplus/msm8974/drivers/video/msm/mdss/mdss_livedisplay.c:145 mdss_livedisplay_worker+0x494/0x580() [] (unwind_backtrace+0x0/0x160) from [] (warn_slowpath_null+0x40/0x64) [] (warn_slowpath_null+0x40/0x64) from [] (mdss_livedisplay_worker+0x494/0x580) [] (mdss_livedisplay_worker+0x494/0x580) from [] (process_one_work+0x174/0x4f4) [] (process_one_work+0x174/0x4f4) from [] (worker_thread+0x1ac/0x44c) [] (worker_thread+0x1ac/0x44c) from [] (kthread+0xc8/0xd4) [] (kthread+0xc8/0xd4) from [] (kernel_thread_exit+0x0/0x8) OPO-713 Change-Id: I0f0ca1bdfd27f2196b4ab9d978653a39515547d3 Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_livedisplay.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_livedisplay.c b/drivers/video/msm/mdss/mdss_livedisplay.c index ab63904a18f8b..d34f5ced902d5 100644 --- a/drivers/video/msm/mdss/mdss_livedisplay.c +++ b/drivers/video/msm/mdss/mdss_livedisplay.c @@ -413,8 +413,6 @@ static ssize_t mdss_livedisplay_set_aco(struct device *dev, mdss_livedisplay_update(mlc, MODE_AUTO_CONTRAST); } - mutex_unlock(&mlc->lock); - return count; } From 25d59857663b4ece084a51449b4c97391bd44d40 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Tue, 1 Aug 2017 15:49:20 -0700 Subject: [PATCH 054/296] binder: always allocate/map first BINDER_MIN_ALLOC pages Certain usecases like camera are constantly allocating and freeing binder buffers beyond the first 4k resulting in mmap_sem contention. If we expand the allocated range from 4k to something higher, we can reduce the contention. Tests show that 6 pages is enough to cause very little update_page_range operations and reduces contention. Bug: 36727951 Change-Id: I28bc3fb9b33c764c257e28487712fce2a3c1078b Reported-by: Tim Murray Signed-off-by: Joel Fernandes Pre-allocate 1 instead of 6 pages as in the original patch, as we use this pre-allocated page to prevent the first page from getting unpinned after removing the buffer headers, rather than pinning pages to speedup larger transactions. Change-Id: I7c3e4884a9538ecfd86601d31c5bcfd6611d37a4 Signed-off-by: Sherry Yang Signed-off-by: anupritaisno1 --- drivers/android/binder.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 5542a6121c6e1..e2e2a99ff3bd1 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -50,6 +50,8 @@ #include #include "binder_trace.h" +#define BINDER_MIN_ALLOC (1 * PAGE_SIZE) + static HLIST_HEAD(binder_devices); static struct dentry *binder_debugfs_dir_entry_root; @@ -658,9 +660,9 @@ static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc, return NULL; } -static int binder_update_page_range(struct binder_proc *proc, int allocate, - void *start, void *end, - struct vm_area_struct *vma) +static int __binder_update_page_range(struct binder_proc *proc, int allocate, + void *start, void *end, + struct vm_area_struct *vma) { void *page_addr; unsigned long user_page_addr; @@ -770,6 +772,20 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, return -ENOMEM; } +static int binder_update_page_range(struct binder_proc *proc, int allocate, + void *start, void *end, + struct vm_area_struct *vma) +{ + /* + * For regular updates, move up start if needed since MIN_ALLOC pages + * are always mapped + */ + if (start - proc->buffer < BINDER_MIN_ALLOC) + start = proc->buffer + BINDER_MIN_ALLOC; + + return __binder_update_page_range(proc, allocate, start, end, vma); +} + static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, size_t data_size, size_t offsets_size, @@ -3565,7 +3581,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) /* binder_update_page_range assumes preemption is disabled */ preempt_disable(); - ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma); + ret = __binder_update_page_range(proc, 1, proc->buffer, + proc->buffer + BINDER_MIN_ALLOC, vma); preempt_enable_no_resched(); if (ret) { ret = -ENOMEM; From c21d25fe55f177c412745875115abd65bf8c6ebd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 29 Jan 2018 17:02:22 -0800 Subject: [PATCH 055/296] array_index_nospec: Sanitize speculative array de-references array_index_nospec() is proposed as a generic mechanism to mitigate against Spectre-variant-1 attacks, i.e. an attack that bypasses boundary checks via speculative execution. The array_index_nospec() implementation is expected to be safe for current generation CPUs across multiple architectures (ARM, x86). Based on an original implementation by Linus Torvalds, tweaked to remove speculative flows by Alexei Starovoitov, and tweaked again by Linus to introduce an x86 assembly implementation for the mask generation. Change-Id: Id28afe5e117369ab52a9400c2cb7d639630f3a08 Co-developed-by: Linus Torvalds Co-developed-by: Alexei Starovoitov Suggested-by: Cyril Novikov Signed-off-by: Dan Williams Signed-off-by: Thomas Gleixner Cc: linux-arch@vger.kernel.org Cc: kernel-hardening@lists.openwall.com Cc: Peter Zijlstra Cc: Catalin Marinas Cc: Will Deacon Cc: Russell King Cc: gregkh@linuxfoundation.org Cc: torvalds@linux-foundation.org Cc: alan@linux.intel.com Link: https://lkml.kernel.org/r/151727414229.33451.18411580953862676575.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: anupritaisno1 --- include/linux/nospec.h | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 include/linux/nospec.h diff --git a/include/linux/nospec.h b/include/linux/nospec.h new file mode 100644 index 0000000000000..b99bced39ac2f --- /dev/null +++ b/include/linux/nospec.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Linus Torvalds. All rights reserved. +// Copyright(c) 2018 Alexei Starovoitov. All rights reserved. +// Copyright(c) 2018 Intel Corporation. All rights reserved. + +#ifndef _LINUX_NOSPEC_H +#define _LINUX_NOSPEC_H + +/** + * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise + * @index: array element index + * @size: number of elements in array + * + * When @index is out of bounds (@index >= @size), the sign bit will be + * set. Extend the sign bit to all bits and invert, giving a result of + * zero for an out of bounds index, or ~0 if within bounds [0, @size). + */ +#ifndef array_index_mask_nospec +static inline unsigned long array_index_mask_nospec(unsigned long index, + unsigned long size) +{ + /* + * Warn developers about inappropriate array_index_nospec() usage. + * + * Even if the CPU speculates past the WARN_ONCE branch, the + * sign bit of @index is taken into account when generating the + * mask. + * + * This warning is compiled out when the compiler can infer that + * @index and @size are less than LONG_MAX. + */ + if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX, + "array_index_nospec() limited to range of [0, LONG_MAX]\n")) + return 0; + + /* + * Always calculate and emit the mask even if the compiler + * thinks the mask is not needed. The compiler does not take + * into account the value of @index under speculation. + */ + OPTIMIZER_HIDE_VAR(index); + return ~(long)(index | (size - 1UL - index)) >> (BITS_PER_LONG - 1); +} +#endif + +/* + * array_index_nospec - sanitize an array index after a bounds check + * + * For a code sequence like: + * + * if (index < size) { + * index = array_index_nospec(index, size); + * val = array[index]; + * } + * + * ...if the CPU speculates past the bounds check then + * array_index_nospec() will clamp the index within the range of [0, + * size). + */ +#define array_index_nospec(index, size) \ +({ \ + typeof(index) _i = (index); \ + typeof(size) _s = (size); \ + unsigned long _mask = array_index_mask_nospec(_i, _s); \ + \ + BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \ + BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \ + \ + _i &= _mask; \ + _i; \ +}) +#endif /* _LINUX_NOSPEC_H */ From 45a075b5a8724d880a42fecdfcc147770924e345 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 5 Feb 2018 14:16:06 +0000 Subject: [PATCH 056/296] nospec: Move array_index_nospec() parameter checking into separate macro For architectures providing their own implementation of array_index_mask_nospec() in asm/barrier.h, attempting to use WARN_ONCE() to complain about out-of-range parameters using WARN_ON() results in a mess of mutually-dependent include files. Rather than unpick the dependencies, simply have the core code in nospec.h perform the checking for us. Change-Id: I36857abc8226fcdfd9c9014e3ffee17f9dc2cbca Signed-off-by: Will Deacon Acked-by: Thomas Gleixner Cc: Dan Williams Cc: Linus Torvalds Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1517840166-15399-1-git-send-email-will.deacon@arm.com Signed-off-by: Ingo Molnar Signed-off-by: anupritaisno1 --- include/linux/nospec.h | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/include/linux/nospec.h b/include/linux/nospec.h index b99bced39ac2f..fbc98e2c8228d 100644 --- a/include/linux/nospec.h +++ b/include/linux/nospec.h @@ -19,20 +19,6 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, unsigned long size) { - /* - * Warn developers about inappropriate array_index_nospec() usage. - * - * Even if the CPU speculates past the WARN_ONCE branch, the - * sign bit of @index is taken into account when generating the - * mask. - * - * This warning is compiled out when the compiler can infer that - * @index and @size are less than LONG_MAX. - */ - if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX, - "array_index_nospec() limited to range of [0, LONG_MAX]\n")) - return 0; - /* * Always calculate and emit the mask even if the compiler * thinks the mask is not needed. The compiler does not take @@ -43,6 +29,26 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, } #endif +/* + * Warn developers about inappropriate array_index_nospec() usage. + * + * Even if the CPU speculates past the WARN_ONCE branch, the + * sign bit of @index is taken into account when generating the + * mask. + * + * This warning is compiled out when the compiler can infer that + * @index and @size are less than LONG_MAX. + */ +#define array_index_mask_nospec_check(index, size) \ +({ \ + if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX, \ + "array_index_nospec() limited to range of [0, LONG_MAX]\n")) \ + _mask = 0; \ + else \ + _mask = array_index_mask_nospec(index, size); \ + _mask; \ +}) + /* * array_index_nospec - sanitize an array index after a bounds check * @@ -61,7 +67,7 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, ({ \ typeof(index) _i = (index); \ typeof(size) _s = (size); \ - unsigned long _mask = array_index_mask_nospec(_i, _s); \ + unsigned long _mask = array_index_mask_nospec_check(_i, _s); \ \ BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \ BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \ From 5f372aa15db7a4256a0d6adad53a653c3c1f5811 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 Feb 2018 13:20:42 -0800 Subject: [PATCH 057/296] nospec: Kill array_index_nospec_mask_check() There are multiple problems with the dynamic sanity checking in array_index_nospec_mask_check(): * It causes unnecessary overhead in the 32-bit case since integer sized @index values will no longer cause the check to be compiled away like in the 64-bit case. * In the 32-bit case it may trigger with user controllable input when the expectation is that should only trigger during development of new kernel enabling. * The macro reuses the input parameter in multiple locations which is broken if someone passes an expression like 'index++' to array_index_nospec(). Change-Id: I845dbcb830b4d72cf7f0b835bfa64476d3763268 Reported-by: Linus Torvalds Signed-off-by: Dan Williams Cc: Andy Lutomirski Cc: Arjan van de Ven Cc: Borislav Petkov Cc: Dave Hansen Cc: David Woodhouse Cc: Greg Kroah-Hartman Cc: Josh Poimboeuf Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Will Deacon Cc: linux-arch@vger.kernel.org Link: http://lkml.kernel.org/r/151881604278.17395.6605847763178076520.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Ingo Molnar Signed-off-by: anupritaisno1 --- include/linux/nospec.h | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/include/linux/nospec.h b/include/linux/nospec.h index fbc98e2c8228d..d6701e34424f1 100644 --- a/include/linux/nospec.h +++ b/include/linux/nospec.h @@ -29,26 +29,6 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, } #endif -/* - * Warn developers about inappropriate array_index_nospec() usage. - * - * Even if the CPU speculates past the WARN_ONCE branch, the - * sign bit of @index is taken into account when generating the - * mask. - * - * This warning is compiled out when the compiler can infer that - * @index and @size are less than LONG_MAX. - */ -#define array_index_mask_nospec_check(index, size) \ -({ \ - if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX, \ - "array_index_nospec() limited to range of [0, LONG_MAX]\n")) \ - _mask = 0; \ - else \ - _mask = array_index_mask_nospec(index, size); \ - _mask; \ -}) - /* * array_index_nospec - sanitize an array index after a bounds check * @@ -67,7 +47,7 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, ({ \ typeof(index) _i = (index); \ typeof(size) _s = (size); \ - unsigned long _mask = array_index_mask_nospec_check(_i, _s); \ + unsigned long _mask = array_index_mask_nospec(_i, _s); \ \ BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \ BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \ From 54d92a1f8c9c601fb485641ada9d95672da39bf1 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 16 Feb 2018 13:20:48 -0800 Subject: [PATCH 058/296] nospec: Allow index argument to have const-qualified type The last expression in a statement expression need not be a bare variable, quoting gcc docs The last thing in the compound statement should be an expression followed by a semicolon; the value of this subexpression serves as the value of the entire construct. and we already use that in e.g. the min/max macros which end with a ternary expression. This way, we can allow index to have const-qualified type, which will in some cases avoid the need for introducing a local copy of index of non-const qualified type. That, in turn, can prevent readers not familiar with the internals of array_index_nospec from wondering about the seemingly redundant extra variable, and I think that's worthwhile considering how confusing the whole _nospec business is. The expression _i&_mask has type unsigned long (since that is the type of _mask, and the BUILD_BUG_ONs guarantee that _i will get promoted to that), so in order not to change the type of the whole expression, add a cast back to typeof(_i). Change-Id: I1fcaea6268a40e10c67d58e906d757b5bce7e8d0 Signed-off-by: Rasmus Villemoes Signed-off-by: Dan Williams Acked-by: Linus Torvalds Cc: Andy Lutomirski Cc: Arjan van de Ven Cc: Borislav Petkov Cc: Dave Hansen Cc: David Woodhouse Cc: Greg Kroah-Hartman Cc: Josh Poimboeuf Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Will Deacon Cc: linux-arch@vger.kernel.org Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/151881604837.17395.10812767547837568328.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Ingo Molnar Signed-off-by: anupritaisno1 --- include/linux/nospec.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/nospec.h b/include/linux/nospec.h index d6701e34424f1..172a19dc35ab2 100644 --- a/include/linux/nospec.h +++ b/include/linux/nospec.h @@ -52,7 +52,6 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \ BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \ \ - _i &= _mask; \ - _i; \ + (typeof(_i)) (_i & _mask); \ }) #endif /* _LINUX_NOSPEC_H */ From b442486c7a7dcb05b87859cfa97a9c6b5bef62a7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 Feb 2018 13:20:54 -0800 Subject: [PATCH 059/296] nospec: Include dependency The nospec.h header expects the per-architecture header file to optionally define array_index_mask_nospec(). Include that dependency to prevent inadvertent fallback to the default array_index_mask_nospec() implementation. The default implementation may not provide a full mitigation on architectures that perform data value speculation. Change-Id: Ib3bb49179541ca4f187f7ec6b91603a58db358f7 Reported-by: Christian Borntraeger Signed-off-by: Dan Williams Cc: Andy Lutomirski Cc: Arjan van de Ven Cc: Borislav Petkov Cc: Dave Hansen Cc: David Woodhouse Cc: Greg Kroah-Hartman Cc: Josh Poimboeuf Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Will Deacon Cc: linux-arch@vger.kernel.org Link: http://lkml.kernel.org/r/151881605404.17395.1341935530792574707.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Ingo Molnar Signed-off-by: anupritaisno1 --- include/linux/nospec.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/nospec.h b/include/linux/nospec.h index 172a19dc35ab2..e791ebc65c9c0 100644 --- a/include/linux/nospec.h +++ b/include/linux/nospec.h @@ -5,6 +5,7 @@ #ifndef _LINUX_NOSPEC_H #define _LINUX_NOSPEC_H +#include /** * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise From b48aa3b9ec10306afb1087e354654f38365e76c3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 15 Feb 2018 17:21:55 +0100 Subject: [PATCH 060/296] posix-timers: Protect posix clock array access against speculation The clockid argument of clockid_to_kclock() comes straight from user space via various syscalls and is used as index into the posix_clocks array. Protect it against spectre v1 array out of bounds speculation. Remove the redundant check for !posix_clock[id] as this is another source for speculation and does not provide any advantage over the return posix_clock[id] path which returns NULL in that case anyway. Change-Id: I46735b66e2b769d750e9484eb75cd2e57f1190d1 Signed-off-by: Thomas Gleixner Acked-by: Peter Zijlstra (Intel) Acked-by: Dan Williams Cc: Rasmus Villemoes Cc: Greg KH Cc: stable@vger.kernel.org Cc: Linus Torvalds Cc: David Woodhouse Link: https://lkml.kernel.org/r/alpine.DEB.2.21.1802151718320.1296@nanos.tec.linutronix.de Signed-off-by: anupritaisno1 --- kernel/posix-timers.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 77e6b83c04315..ea6f6a49eeca5 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -48,6 +48,7 @@ #include #include #include +#include /* * Management arrays for POSIX timers. Timers are now kept in static hash table @@ -573,13 +574,21 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set) static struct k_clock *clockid_to_kclock(const clockid_t id) { - if (id < 0) + struct k_clock *kc; + clockid_t idx = id; + + if (id < 0) { return (id & CLOCKFD_MASK) == CLOCKFD ? &clock_posix_dynamic : &clock_posix_cpu; + } + + if (id >= ARRAY_SIZE(posix_clocks)) + return NULL; - if (id >= MAX_CLOCKS || !posix_clocks[id].clock_getres) + kc = &posix_clocks[array_index_nospec(idx, ARRAY_SIZE(posix_clocks))]; + if (!kc->clock_getres) return NULL; - return &posix_clocks[id]; + return kc; } static int common_timer_create(struct k_itimer *new_timer) From c9c2d4aa0fc0ad7bac33ed28c2d7bc79dd04e0e8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 4 Jun 2018 09:32:02 +0200 Subject: [PATCH 061/296] vfs, fdtable: Prevent bounds-check bypass via speculative execution 'fd' is a user controlled value that is used as a data dependency to read from the 'fdt->fd' array. In order to avoid potential leaks of kernel memory values, block speculative execution of the instruction stream that could issue reads based on an invalid 'file *' returned from __fcheck_files. Change-Id: I148b33be036906d37393e49391d9181e5449f061 Co-developed-by: Elena Reshetova Signed-off-by: Dan Williams Signed-off-by: Thomas Gleixner Cc: linux-arch@vger.kernel.org Cc: kernel-hardening@lists.openwall.com Cc: gregkh@linuxfoundation.org Cc: Al Viro Cc: torvalds@linux-foundation.org Cc: alan@linux.intel.com Link: https://lkml.kernel.org/r/151727418500.33451.17392199002892248656.stgi t@dwillia2-desk3.amr.corp.intel.com Signed-off-by: anupritaisno1 --- include/linux/fdtable.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h index 085197bd88120..6ed63824f82ae 100644 --- a/include/linux/fdtable.h +++ b/include/linux/fdtable.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -79,8 +80,10 @@ static inline struct file * fcheck_files(struct files_struct *files, unsigned in struct file * file = NULL; struct fdtable *fdt = files_fdtable(files); - if (fd < fdt->max_fds) + if (fd < fdt->max_fds) { + fd = array_index_nospec(fd, fdt->max_fds); file = rcu_dereference_check_fdtable(files, fdt->fd[fd]); + } return file; } From 7faabbc683ff1e1c099fc80d3f93950bc289afa9 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 29 Jan 2018 17:03:15 -0800 Subject: [PATCH 062/296] nl80211: Sanitize array index in parse_txq_params Wireless drivers rely on parse_txq_params to validate that txq_params->ac is less than NL80211_NUM_ACS by the time the low-level driver's ->conf_tx() handler is called. Use a new helper, array_index_nospec(), to sanitize txq_params->ac with respect to speculation. I.e. ensure that any speculation into ->conf_tx() handlers is done with a value of txq_params->ac that is within the bounds of [0, NL80211_NUM_ACS). Change-Id: I4e81dbf507ba0f9bbde93116bc8d0cfee6ac58cc Reported-by: Christian Lamparter Reported-by: Elena Reshetova Signed-off-by: Dan Williams Signed-off-by: Thomas Gleixner Acked-by: Johannes Berg Cc: linux-arch@vger.kernel.org Cc: kernel-hardening@lists.openwall.com Cc: gregkh@linuxfoundation.org Cc: linux-wireless@vger.kernel.org Cc: torvalds@linux-foundation.org Cc: "David S. Miller" Cc: alan@linux.intel.com Link: https://lkml.kernel.org/r/151727419584.33451.7700736761686184303.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: anupritaisno1 --- net/wireless/nl80211.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index dc9a746b06f91..7d169ed1337fc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1762,20 +1763,22 @@ static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { static int parse_txq_params(struct nlattr *tb[], struct ieee80211_txq_params *txq_params) { + u8 ac; + if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] || !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || !tb[NL80211_TXQ_ATTR_AIFS]) return -EINVAL; - txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]); + ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]); txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); - if (txq_params->ac >= NL80211_NUM_ACS) + if (ac >= NL80211_NUM_ACS) return -EINVAL; - + txq_params->ac = array_index_nospec(ac, NL80211_NUM_ACS); return 0; } From e573a6d5d549f3bc92385359e0ed5a1192537724 Mon Sep 17 00:00:00 2001 From: Haibin Liu Date: Sat, 2 Dec 2017 02:11:35 +0800 Subject: [PATCH 063/296] msm: sensor: actuator: check NULL pointer for actuator ctrl Add NULL pointer checking for actuator ctrl. CRs-Fixed: 2152399 Bug: 65122765 Change-Id: I2e206516e5ea1383c7396625455e1d7403df51cf Signed-off-by: Haibin Liu --- .../camera_v2/sensor/actuator/msm_actuator.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) mode change 100755 => 100644 drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c 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 old mode 100755 new mode 100644 index cde09aff4f2f1..c42f49bf8f2b8 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -125,21 +125,18 @@ static int32_t msm_actuator_piezo_set_default_focus( static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, int16_t next_lens_position, uint32_t hw_params, uint16_t delay) { - struct msm_actuator_reg_params_t *write_arr = a_ctrl->reg_tbl; + struct msm_actuator_reg_params_t *write_arr = NULL; uint32_t hw_dword = hw_params; uint16_t i2c_byte1 = 0, i2c_byte2 = 0; uint16_t value = 0; - uint32_t size = a_ctrl->reg_tbl_size, i = 0; - struct msm_camera_i2c_reg_array *i2c_tbl = a_ctrl->i2c_reg_tbl; + uint32_t size = 0, i = 0; + struct msm_camera_i2c_reg_array *i2c_tbl = NULL; CDBG("Enter\n"); - if (a_ctrl == NULL) { - pr_err("failed. actuator ctrl is NULL"); - return; - } - - if (a_ctrl->i2c_reg_tbl == NULL) { - pr_err("failed. i2c reg tabl is NULL"); + if ((!a_ctrl) || + (!a_ctrl->reg_tbl) || + (!a_ctrl->i2c_reg_tbl)) { + pr_err("failed. NULL actuator pointers"); return; } From 5b78a00d215b294aae7a1b6ec2e2d019e26a5d9f Mon Sep 17 00:00:00 2001 From: Haibin Liu Date: Tue, 5 Dec 2017 15:06:18 +0800 Subject: [PATCH 064/296] 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 Bug: 65122765 Change-Id: Ieec3d88e6dae0177787da0906f53d59ac4f5a624 Signed-off-by: Haibin Liu Signed-off-by: VijayaKumar T M --- .../camera_v2/sensor/actuator/msm_actuator.c | 35 +++++++++++++++++-- 1 file changed, 33 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 c42f49bf8f2b8..f54fbb9a8023e 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 @@ -98,6 +98,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, @@ -614,6 +619,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); @@ -684,6 +694,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); @@ -805,6 +819,10 @@ static int32_t msm_actuator_bivcm_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); @@ -1203,6 +1221,18 @@ static int32_t msm_actuator_set_position( return -EFAULT; } + if (!a_ctrl || !a_ctrl->func_tbl || + !a_ctrl->func_tbl->actuator_parse_i2c_params || + !a_ctrl->i2c_reg_tbl) { + pr_err("failed. NULL actuator pointers."); + return -EFAULT; + } + + if (a_ctrl->actuator_state != ACT_OPS_ACTIVE) { + pr_err("failed. Invalid actuator state."); + return -EFAULT; + } + a_ctrl->i2c_tbl_index = 0; for (index = 0; index < set_pos->number_of_steps; index++) { next_lens_position = set_pos->pos[index]; @@ -1322,12 +1352,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; } @@ -1358,6 +1386,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 = kzalloc(sizeof(struct msm_camera_i2c_reg_array) * @@ -1367,6 +1396,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 * From 6fc3fd7a1e6ce6d5f73700783d388389997d6ad4 Mon Sep 17 00:00:00 2001 From: Haibin Liu Date: Thu, 28 Dec 2017 20:42:37 +0800 Subject: [PATCH 065/296] 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 Bug: 65122765 Change-Id: I141bba45662769f0661c947fb642c2671578f32e Signed-off-by: Haibin Liu Signed-off-by: VijayaKumar T M --- .../platform/msm/camera_v2/sensor/actuator/msm_actuator.c | 6 +++++- 1 file changed, 5 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 f54fbb9a8023e..e3c81f3396bfa 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-2015, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-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 @@ -709,6 +709,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]; @@ -834,6 +836,8 @@ static int32_t msm_actuator_bivcm_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]; From aa3db7a66b1f1ae72cf06be04dcd1d34e70b1e90 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 23 Oct 2017 16:46:00 -0700 Subject: [PATCH 066/296] 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. Bug: 69916367 Change-Id: Ic6a2fda77685b90d7642c09b20f5d57d6d48b726 Reported-by: Andrey Konovalov Reviewed-by: Andrey Konovalov Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman Signed-off-by: Siqi Lin --- 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 a9f8f925ba2b2..5a7be4c559f3b 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -232,13 +232,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]; @@ -246,8 +250,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; } From 0cc43eb0f6896511f25d04dcdc9064e8c7545dee Mon Sep 17 00:00:00 2001 From: Benjamin Chan Date: Tue, 14 Nov 2017 00:27:17 -0500 Subject: [PATCH 067/296] 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. Bug: 71501679 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 d8878ff6a2ae0..b17c66ef69773 100644 --- a/drivers/video/msm/mdss/mdss_mdp_util.c +++ b/drivers/video/msm/mdss/mdss_mdp_util.c @@ -438,6 +438,8 @@ int mdss_mdp_get_plane_sizes(u32 format, 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; @@ -446,7 +448,6 @@ int mdss_mdp_get_plane_sizes(u32 format, u32 w, u32 h, return -EINVAL; bpp = fmt->bpp; - memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes)); if (bwc_mode) { u32 height, meta_size; From 99e34a96d1c296f627e6a33d3a8f5709bd0154b2 Mon Sep 17 00:00:00 2001 From: Sean Callanan Date: Mon, 2 Apr 2018 19:11:23 -0700 Subject: [PATCH 068/296] msm: ADSPRPC: use access_ok to validate pointers The FASTRPC_IOCTL_INIT ioctl registers a pointer for later access without checking that it is a user pointer. This could allow arbitrary kernel memory access. This patch verifies that the pointer is a user pointer. Bug: 63165064 Change-Id: I936f73a2c2029f9e7ca12cc8fc06d0698e6710c0 Signed-off-by: Tharun Kumar Merugu Signed-off-by: Sean Callanan Signed-off-by: anupritaisno1 --- drivers/char/adsprpc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 815d822565a7f..7e28ca8759623 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1400,6 +1400,12 @@ static int fastrpc_init_process(struct file_data *fdata, inbuf.pgid = current->tgid; inbuf.namelen = strlen(current->comm); inbuf.filelen = init->filelen; + if (!access_ok(0, (void const __user *)init->file, + init->filelen)) + goto bail; + if (!access_ok(1, (void const __user *)init->mem, + init->memlen)) + goto bail; VERIFY(err, 0 == map_buffer(me, fdata, init->memfd, (char *)init->mem, init->memlen, &map, &pages, &npages)); From 7fcfb179f8476367901f7ce6d9766b4e9be0fdcf Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Tue, 12 Jun 2018 10:50:32 +0100 Subject: [PATCH 069/296] op2: dts: store dm-verity state persistently on device Signed-off-by: anupritaisno1 --- arch/arm/boot/dts/qcom/msm8994.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8994.dtsi b/arch/arm/boot/dts/qcom/msm8994.dtsi index 09a274626dcfe..ae288c7dd52bb 100644 --- a/arch/arm/boot/dts/qcom/msm8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm8994.dtsi @@ -37,7 +37,7 @@ dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/system"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; - fsmgr_flags = "wait,verify"; + fsmgr_flags = "wait,verify==/dev/block/platform/soc.0/f9824900.sdhci/by-name/reserve2"; status = "ok"; }; }; From 0fbf12d661d3f1ce4dc374762623d7a9ceab1119 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Tue, 12 Jun 2018 10:53:06 +0100 Subject: [PATCH 070/296] op2: dts: fix derp Signed-off-by: anupritaisno1 --- arch/arm/boot/dts/qcom/msm8994.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8994.dtsi b/arch/arm/boot/dts/qcom/msm8994.dtsi index ae288c7dd52bb..f86775bb1de88 100644 --- a/arch/arm/boot/dts/qcom/msm8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm8994.dtsi @@ -37,7 +37,7 @@ dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/system"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; - fsmgr_flags = "wait,verify==/dev/block/platform/soc.0/f9824900.sdhci/by-name/reserve2"; + fsmgr_flags = "wait,verify=/dev/block/platform/soc.0/f9824900.sdhci/by-name/reserve2"; status = "ok"; }; }; From c7e98778e333e82fa529d30bc6ffef78ca82f006 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 19 Dec 2013 12:09:32 +0100 Subject: [PATCH 071/296] UPSTREAM: HID: Bluetooth: hidp: make sure input buffers are big enough (cherry picked from commit a4b1b5877b514b276f0f31efe02388a9c2836728) HID core expects the input buffers to be at least of size 4096 (HID_MAX_BUFFER_SIZE). Other sizes will result in buffer-overflows if an input-report is smaller than advertised. We could, like i2c, compute the biggest report-size instead of using HID_MAX_BUFFER_SIZE, but this will blow up if report-descriptors are changed after ->start() has been called. So lets be safe and just use the biggest buffer we have. Note that this adds an additional copy to the HIDP input path. If there is a way to make sure the skb-buf is big enough, we should use that instead. The best way would be to make hid-core honor the @size argument, though, that sounds easier than it is. So lets just fix the buffer-overflows for now and afterwards look for a faster way for all transport drivers. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina Bug: 65853588 Change-Id: Iec1410993a6d21dd9c88bd7669cf658b9f48f9dc Signed-off-by: anupritaisno1 --- net/bluetooth/hidp/core.c | 16 ++++++++++++++-- net/bluetooth/hidp/hidp.h | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 14f700398ba05..1f305c4658956 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -425,6 +425,16 @@ static void hidp_del_timer(struct hidp_session *session) del_timer(&session->timer); } +static void hidp_process_report(struct hidp_session *session, + int type, const u8 *data, int len, int intr) +{ + if (len > HID_MAX_BUFFER_SIZE) + len = HID_MAX_BUFFER_SIZE; + + memcpy(session->input_buf, data, len); + hid_input_report(session->hid, type, session->input_buf, len, intr); +} + static void hidp_process_handshake(struct hidp_session *session, unsigned char param) { @@ -498,7 +508,8 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, hidp_input_report(session, skb); if (session->hid) - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); + hidp_process_report(session, HID_INPUT_REPORT, + skb->data, skb->len, 0); break; case HIDP_DATA_RTYPE_OTHER: @@ -580,7 +591,8 @@ static void hidp_recv_intr_frame(struct hidp_session *session, hidp_input_report(session, skb); if (session->hid) { - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1); + hidp_process_report(session, HID_INPUT_REPORT, + skb->data, skb->len, 1); BT_DBG("report len %d", skb->len); } } else { diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 6162ce8606acc..5e2a5d54ad25a 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -24,6 +24,7 @@ #define __HIDP_H #include +#include #include #include #include @@ -177,6 +178,9 @@ struct hidp_session { /* Used in hidp_output_raw_report() */ int output_report_success; /* boolean */ + + /* temporary input buffer */ + u8 input_buf[HID_MAX_BUFFER_SIZE]; }; /* HIDP init defines */ From 58036c5d3b27738b333cb1fcd396b3c93b10019d Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 16 Jan 2018 13:42:30 -0800 Subject: [PATCH 072/296] ANDROID: Bluetooth: hidp: buffer overflow in hidp_process_report The buffer length is unsigned at all layers, but gets cast to int and checked in hidp_process_report and can lead to a buffer overflow. Switch len parameter to unsigned int to resolve issue. Signed-off-by: Mark Salyzyn Fixes: 678af93e46ac10318b54f2f0c9abbdfe75c4e078 ("HID: Bluetooth: hidp: make sure input buffers are big enough") Bug: 65853588 Change-Id: I779ce783ae7c3bce8c5a66c0954ef31347e42cfc Signed-off-by: anupritaisno1 --- net/bluetooth/hidp/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 1f305c4658956..6c761ac119790 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -425,8 +425,8 @@ static void hidp_del_timer(struct hidp_session *session) del_timer(&session->timer); } -static void hidp_process_report(struct hidp_session *session, - int type, const u8 *data, int len, int intr) +static void hidp_process_report(struct hidp_session *session, int type, + const u8 *data, unsigned int len, int intr) { if (len > HID_MAX_BUFFER_SIZE) len = HID_MAX_BUFFER_SIZE; From 78dc902f3585c8e87ed69a610ed2d6632da15945 Mon Sep 17 00:00:00 2001 From: Sreelakshmi Gownipalli Date: Mon, 29 Jan 2018 13:17:13 -0800 Subject: [PATCH 073/296] 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. Bug: 70528036 Change-Id: Id73ed1c8b104428eceef0544ce2858160cc08fd2 Signed-off-by: Sreelakshmi Gownipalli Signed-off-by: anupritaisno1 --- drivers/char/diag/diag_dci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index f954c0c00d2d9..cc0f9d586b040 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -818,7 +818,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; From c5471f08c2c460448a85c338197e9a87c454d23f Mon Sep 17 00:00:00 2001 From: Andrew Chant Date: Fri, 6 Apr 2018 16:14:42 -0700 Subject: [PATCH 074/296] diag: dci: check signed values for negativity when comparing a signed value vs sizeof, make sure it's >= 0 so that when promoted to signed for comparison it won't lead to unexpected results. Bug: 70528036 Change-Id: I99a1220598e67e3ce5c99d425e84c79639121838 Signed-off-by: Andrew Chant Signed-off-by: anupritaisno1 --- drivers/char/diag/diag_dci.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index cc0f9d586b040..57b43001caec6 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -798,11 +798,11 @@ static void dci_process_ctrl_status(unsigned char *buf, int len, int token) { struct diag_ctrl_dci_status *header = NULL; unsigned char *temp = buf; - uint32_t read_len = 0; + unsigned int read_len = 0; uint8_t i; int peripheral_mask, status; - if (!buf || (len < sizeof(struct diag_ctrl_dci_status))) { + if (!buf || len < 2 || (len < sizeof(struct diag_ctrl_dci_status))) { pr_err("diag: In %s, invalid buf %pK or length: %d\n", __func__, buf, len); return; @@ -860,7 +860,9 @@ static void dci_process_ctrl_handshake_pkt(unsigned char *buf, int len, unsigned char *temp = buf; int err = 0; - if (!buf || (len < sizeof(struct diag_ctrl_dci_handshake_pkt))) + if (!buf) + return; + if (len < 0 || len < sizeof(struct diag_ctrl_dci_handshake_pkt)) return; if (!VALID_DCI_TOKEN(token)) From 7ab37aa1eb3acb845e9ecff613ecf15d3b9ec23e Mon Sep 17 00:00:00 2001 From: Tharun Kumar Merugu Date: Tue, 30 Jan 2018 15:43:36 +0530 Subject: [PATCH 075/296] 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. Bug: 74237782 Change-Id: I9cfd10d0c1b25c3085b8e15c7ca1c8ff214bf10d Acked-by: Viswanatham Paduchuri Signed-off-by: Tharun Kumar Merugu Signed-off-by: Steve Pfetsch Signed-off-by: anupritaisno1 --- drivers/char/adsprpc.c | 73 ++++++++++++++++++++++++++++++++--- drivers/char/adsprpc_shared.h | 4 +- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 7e28ca8759623..258c3c9782675 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -62,6 +62,9 @@ up_read(¤t->mm->mmap_sem);\ } while (0) +#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) @@ -91,6 +94,13 @@ static inline uint32_t buf_page_size(uint32_t size) return sz > PAGE_SIZE ? sz : PAGE_SIZE; } +static inline uint64_t ptr_to_uint64(void *ptr) +{ + uint64_t addr = (uint64_t)((uintptr_t)ptr); + + return addr; +} + static inline int buf_get_pages(void *addr, ssize_t sz, int nr_pages, int access, struct smq_phy_page *pages, int nr_elems, struct smq_phy_page *range) @@ -178,6 +188,8 @@ struct smq_invoke_ctx { uint32_t sc; struct overlap *overs; struct overlap **overps; + unsigned int magic; + uint64_t ctxid; }; struct smq_context_list { @@ -216,6 +228,8 @@ struct fastrpc_apps { spinlock_t wrlock; spinlock_t hlock; struct hlist_head htbl[RPC_HASH_SZ]; + spinlock_t ctxlock; + struct smq_invoke_ctx *ctxtable[FASTRPC_CTX_MAX]; }; struct fastrpc_mmap { @@ -507,9 +521,10 @@ static int context_alloc(struct fastrpc_apps *me, uint32_t kernel, struct file_data *fdata, struct smq_invoke_ctx **po) { - int err = 0, bufs, size = 0; + int err = 0, bufs, ii, size = 0; struct smq_invoke_ctx *ctx = 0; struct smq_context_list *clst = &me->clst; + struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; bufs = REMOTE_SCALARS_INBUFS(invoke->sc) + @@ -563,10 +578,26 @@ static int context_alloc(struct fastrpc_apps *me, uint32_t kernel, ctx->pid = current->pid; ctx->tgid = current->tgid; init_completion(&ctx->work); + ctx->magic = FASTRPC_CTX_MAGIC; spin_lock(&clst->hlock); hlist_add_head(&ctx->hn, &clst->pending); spin_unlock(&clst->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) @@ -594,6 +625,7 @@ static void context_free(struct smq_invoke_ctx *ctx, int remove) int ssrcount = ctx->fdata->ssrcount; struct fastrpc_smmu *smmu = &apps->channel[cid].smmu; struct fastrpc_buf *b; + struct fastrpc_apps *me = &gfa; int i, bufs; if (ctx->smmu) { bufs = REMOTE_SCALARS_INBUFS(ctx->sc) + @@ -615,6 +647,17 @@ static void context_free(struct smq_invoke_ctx *ctx, int remove) free_mem(b, ctx->fdata); kfree(ctx->abufs); + 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); if (ctx->dev) { add_dev(apps, ctx->dev); if (ctx->obuf.handle != ctx->dev->buf.handle) @@ -1093,7 +1136,7 @@ static int fastrpc_invoke_send(struct fastrpc_apps *me, msg.tid = current->pid; if (kernel) msg.pid = 0; - msg.invoke.header.ctx = ctx; + msg.invoke.header.ctx = ctx->ctxid; msg.invoke.header.handle = handle; msg.invoke.header.sc = sc; msg.invoke.page.addr = buf->phys; @@ -1123,16 +1166,35 @@ static void fastrpc_deinit(void) static void fastrpc_read_handler(int cid) { struct fastrpc_apps *me = &gfa; - struct smq_invoke_rsp rsp; - int ret = 0; + struct smq_invoke_rsp rsp = {0}; + 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; - context_notify_user(rsp.ctx, rsp.retval); + 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(me->ctxtable[index], 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) @@ -1163,6 +1225,7 @@ static int fastrpc_init(void) spin_lock_init(&me->hlock); spin_lock_init(&me->wrlock); + spin_lock_init(&me->ctxlock); mutex_init(&me->smd_mutex); context_list_ctor(&me->clst); for (i = 0; i < RPC_HASH_SZ; ++i) diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h index c0f8d3cc8c4e3..c8e31369cef16 100644 --- a/drivers/char/adsprpc_shared.h +++ b/drivers/char/adsprpc_shared.h @@ -137,7 +137,7 @@ struct fastrpc_ioctl_mmap { }; struct smq_null_invoke { - struct smq_invoke_ctx *ctx; /* invoke caller context */ + uint64_t ctx; /* invoke caller context */ uint32_t handle; /* handle to invoke */ uint32_t sc; /* scalars structure describing the data */ }; @@ -164,7 +164,7 @@ struct smq_msg { }; struct smq_invoke_rsp { - struct smq_invoke_ctx *ctx; /* invoke caller context */ + uint64_t ctx; /* invoke caller context */ int retval; /* invoke return value */ }; From c29cb9026d7ba7099a12a6fce1b2a610f840cd59 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:02:10 +0100 Subject: [PATCH 076/296] Revert "Add PowerSuspend v1.5 Driver" This reverts commit 67566ae32ffa9222f7a84b703ed45c7ad88c8afe. Signed-off-by: anupritaisno1 --- include/linux/powersuspend.h | 54 ------ kernel/power/Kconfig | 7 - kernel/power/Makefile | 1 - kernel/power/autosleep.c | 12 -- kernel/power/powersuspend.c | 324 ----------------------------------- 5 files changed, 398 deletions(-) delete mode 100644 include/linux/powersuspend.h delete mode 100644 kernel/power/powersuspend.c diff --git a/include/linux/powersuspend.h b/include/linux/powersuspend.h deleted file mode 100644 index 5d187022fe89d..0000000000000 --- a/include/linux/powersuspend.h +++ /dev/null @@ -1,54 +0,0 @@ -/* include/linux/powersuspend.h - * - * Copyright (C) 2007-2008 Google, Inc. - * Copyright (C) 2013 Paul Reioux - * - * Modified by Jean-Pierre Rasquin - * - * v1.1 - make powersuspend not depend on a userspace initiator anymore, - * but use a hook in autosleep instead. - * - * v1.2 - make kernel / userspace mode switchable - * - * v1.3 - add a hook in display panel driver as alternative kernel trigger - * - * v1.4 - add a hybrid-kernel mode, accepting both kernel hooks (first wins) - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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_POWERSUSPEND_H -#define _LINUX_POWERSUSPEND_H - -#include - -#define POWER_SUSPEND_INACTIVE 0 -#define POWER_SUSPEND_ACTIVE 1 - -#define POWER_SUSPEND_AUTOSLEEP 0 // Use kernel autosleep as hook -#define POWER_SUSPEND_USERSPACE 1 // Use fauxclock as trigger -#define POWER_SUSPEND_PANEL 2 // Use display panel state as hook -#define POWER_SUSPEND_HYBRID 3 // Use display panel state and autosleep as hook - -struct power_suspend { - struct list_head link; - void (*suspend)(struct power_suspend *h); - void (*resume)(struct power_suspend *h); -}; - -void register_power_suspend(struct power_suspend *handler); -void unregister_power_suspend(struct power_suspend *handler); - -void set_power_suspend_state_autosleep_hook(int new_state); -void set_power_suspend_state_panel_hook(int new_state); - -#endif - diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index ac8d61a3a2819..1299996387a38 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -26,13 +26,6 @@ config WAKELOCK bool default y -config POWERSUSPEND - bool "Power suspend" - default y - ---help--- - Call early suspend handlers when the user requested sleep state - changes. - config HIBERNATE_CALLBACKS bool diff --git a/kernel/power/Makefile b/kernel/power/Makefile index fb7736b57b01e..fdca558be0a35 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o -obj-$(CONFIG_POWERSUSPEND) += powersuspend.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o diff --git a/kernel/power/autosleep.c b/kernel/power/autosleep.c index 351ceb5244300..9012ecf7b814f 100644 --- a/kernel/power/autosleep.c +++ b/kernel/power/autosleep.c @@ -10,10 +10,6 @@ #include #include -#ifdef CONFIG_POWERSUSPEND -#include -#endif - #include "power.h" static suspend_state_t autosleep_state; @@ -109,16 +105,8 @@ int pm_autosleep_set_state(suspend_state_t state) if (state > PM_SUSPEND_ON) { pm_wakep_autosleep_enabled(true); queue_up_suspend_work(); -#ifdef CONFIG_POWERSUSPEND - // Yank555.lu : add hook to handle powersuspend tasks (sleep) - set_power_suspend_state_autosleep_hook(POWER_SUSPEND_ACTIVE); -#endif } else { pm_wakep_autosleep_enabled(false); -#ifdef CONFIG_POWERSUSPEND - // Yank555.lu : add hook to handle powersuspend tasks (wakeup) - set_power_suspend_state_autosleep_hook(POWER_SUSPEND_INACTIVE); -#endif } mutex_unlock(&autosleep_lock); diff --git a/kernel/power/powersuspend.c b/kernel/power/powersuspend.c deleted file mode 100644 index 2b7fe5059dbbb..0000000000000 --- a/kernel/power/powersuspend.c +++ /dev/null @@ -1,324 +0,0 @@ -/* kernel/power/powersuspend.c - * - * Copyright (C) 2005-2008 Google, Inc. - * Copyright (C) 2013 Paul Reioux - * - * Modified by Jean-Pierre Rasquin - * - * v1.1 - make powersuspend not depend on a userspace initiator anymore, - * but use a hook in autosleep instead. - * - * v1.2 - make kernel / userspace mode switchable - * - * v1.3 - add a hook in display panel driver as alternative kernel trigger - * - * v1.4 - add a hybrid-kernel mode, accepting both kernel hooks (first wins) - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 - -#define MAJOR_VERSION 1 -#define MINOR_VERSION 5 - -//#define POWER_SUSPEND_DEBUG // Add debugging prints in dmesg - -struct workqueue_struct *suspend_work_queue; - -static DEFINE_MUTEX(power_suspend_lock); -static LIST_HEAD(power_suspend_handlers); -static void power_suspend(struct work_struct *work); -static void power_resume(struct work_struct *work); -static DECLARE_WORK(power_suspend_work, power_suspend); -static DECLARE_WORK(power_resume_work, power_resume); -static DEFINE_SPINLOCK(state_lock); - -static int state; // Yank555.lu : Current powersave state (screen on / off) -static int mode; // Yank555.lu : Current powersave mode (kernel / userspace / panel / hybrid) - -void register_power_suspend(struct power_suspend *handler) -{ - struct list_head *pos; - - mutex_lock(&power_suspend_lock); - list_for_each(pos, &power_suspend_handlers) { - struct power_suspend *p; - p = list_entry(pos, struct power_suspend, link); - } - list_add_tail(&handler->link, pos); - mutex_unlock(&power_suspend_lock); -} -EXPORT_SYMBOL(register_power_suspend); - -void unregister_power_suspend(struct power_suspend *handler) -{ - mutex_lock(&power_suspend_lock); - list_del(&handler->link); - mutex_unlock(&power_suspend_lock); -} -EXPORT_SYMBOL(unregister_power_suspend); - -static void power_suspend(struct work_struct *work) -{ - struct power_suspend *pos; - unsigned long irqflags; - int abort = 0; - - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] entering suspend...\n"); - #endif - mutex_lock(&power_suspend_lock); - spin_lock_irqsave(&state_lock, irqflags); - if (state == POWER_SUSPEND_INACTIVE) - abort = 1; - spin_unlock_irqrestore(&state_lock, irqflags); - - if (abort) - goto abort_suspend; - - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] suspending...\n"); - #endif - list_for_each_entry(pos, &power_suspend_handlers, link) { - if (pos->suspend != NULL) { - pos->suspend(pos); - } - } - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] suspend completed.\n"); - #endif -abort_suspend: - mutex_unlock(&power_suspend_lock); -} - -static void power_resume(struct work_struct *work) -{ - struct power_suspend *pos; - unsigned long irqflags; - int abort = 0; - - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] entering resume...\n"); - #endif - mutex_lock(&power_suspend_lock); - spin_lock_irqsave(&state_lock, irqflags); - if (state == POWER_SUSPEND_ACTIVE) - abort = 1; - spin_unlock_irqrestore(&state_lock, irqflags); - - if (abort) - goto abort_resume; - - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] resuming...\n"); - #endif - list_for_each_entry_reverse(pos, &power_suspend_handlers, link) { - if (pos->resume != NULL) { - pos->resume(pos); - } - } - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] resume completed.\n"); - #endif -abort_resume: - mutex_unlock(&power_suspend_lock); -} - -void set_power_suspend_state(int new_state) -{ - unsigned long irqflags; - - spin_lock_irqsave(&state_lock, irqflags); - if (state == POWER_SUSPEND_INACTIVE && new_state == POWER_SUSPEND_ACTIVE) { - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] state activated.\n"); - #endif - state = new_state; - queue_work(suspend_work_queue, &power_suspend_work); - } else if (state == POWER_SUSPEND_ACTIVE && new_state == POWER_SUSPEND_INACTIVE) { - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] state deactivated.\n"); - #endif - state = new_state; - queue_work(suspend_work_queue, &power_resume_work); - } - spin_unlock_irqrestore(&state_lock, irqflags); -} - -void set_power_suspend_state_autosleep_hook(int new_state) -{ - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] autosleep resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup"); - #endif - // Yank555.lu : Only allow autosleep hook changes in autosleep & hybrid mode - if (mode == POWER_SUSPEND_AUTOSLEEP || mode == POWER_SUSPEND_HYBRID) - set_power_suspend_state(new_state); -} - -EXPORT_SYMBOL(set_power_suspend_state_autosleep_hook); - -void set_power_suspend_state_panel_hook(int new_state) -{ - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] panel resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup"); - #endif - // Yank555.lu : Only allow autosleep hook changes in autosleep & hybrid mode - if (mode == POWER_SUSPEND_PANEL || mode == POWER_SUSPEND_HYBRID) - set_power_suspend_state(new_state); -} - -EXPORT_SYMBOL(set_power_suspend_state_panel_hook); - -// ------------------------------------------ sysfs interface ------------------------------------------ - -static ssize_t power_suspend_state_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", state); -} - -static ssize_t power_suspend_state_store(struct kobject *kobj, - struct kobj_attribute *attr, const char *buf, size_t count) -{ - int new_state = 0; - - // Yank555.lu : Only allow sysfs changes from userspace mode - if (mode != POWER_SUSPEND_USERSPACE) - return -EINVAL; - - sscanf(buf, "%d\n", &new_state); - - #ifdef POWER_SUSPEND_DEBUG - pr_info("[POWERSUSPEND] userspace resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup"); - #endif - if(new_state == POWER_SUSPEND_ACTIVE || new_state == POWER_SUSPEND_INACTIVE) - set_power_suspend_state(new_state); - - return count; -} - -static struct kobj_attribute power_suspend_state_attribute = - __ATTR(power_suspend_state, 0666, - power_suspend_state_show, - power_suspend_state_store); - -static ssize_t power_suspend_mode_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", mode); -} - -static ssize_t power_suspend_mode_store(struct kobject *kobj, - struct kobj_attribute *attr, const char *buf, size_t count) -{ - int data = 0; - - sscanf(buf, "%d\n", &data); - - switch (data) { - case POWER_SUSPEND_AUTOSLEEP: - case POWER_SUSPEND_PANEL: - case POWER_SUSPEND_USERSPACE: - case POWER_SUSPEND_HYBRID: mode = data; - return count; - default: - return -EINVAL; - } - -} - -static struct kobj_attribute power_suspend_mode_attribute = - __ATTR(power_suspend_mode, 0666, - power_suspend_mode_show, - power_suspend_mode_store); - -static ssize_t power_suspend_version_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return sprintf(buf, "version: %d.%d\n", MAJOR_VERSION, MINOR_VERSION); -} - -static struct kobj_attribute power_suspend_version_attribute = - __ATTR(power_suspend_version, 0444, - power_suspend_version_show, - NULL); - -static struct attribute *power_suspend_attrs[] = -{ - &power_suspend_state_attribute.attr, - &power_suspend_mode_attribute.attr, - &power_suspend_version_attribute.attr, - NULL, -}; - -static struct attribute_group power_suspend_attr_group = -{ - .attrs = power_suspend_attrs, -}; - -static struct kobject *power_suspend_kobj; - -// ------------------ sysfs interface ----------------------- -static int __init power_suspend_init(void) -{ - - int sysfs_result; - - power_suspend_kobj = kobject_create_and_add("power_suspend", - kernel_kobj); - if (!power_suspend_kobj) { - pr_err("%s kobject create failed!\n", __FUNCTION__); - return -ENOMEM; - } - - sysfs_result = sysfs_create_group(power_suspend_kobj, - &power_suspend_attr_group); - - if (sysfs_result) { - pr_info("%s group create failed!\n", __FUNCTION__); - kobject_put(power_suspend_kobj); - return -ENOMEM; - } - - suspend_work_queue = create_singlethread_workqueue("p-suspend"); - - if (suspend_work_queue == NULL) { - return -ENOMEM; - } - -// mode = POWER_SUSPEND_AUTOSLEEP; // Yank555.lu : Default to autosleep mode -// mode = POWER_SUSPEND_USERSPACE; // Yank555.lu : Default to userspace mode -// mode = POWER_SUSPEND_PANEL; // Yank555.lu : Default to display panel mode - mode = POWER_SUSPEND_HYBRID; // Yank555.lu : Default to display panel / autosleep hybrid mode - - return 0; -} - -static void __exit power_suspend_exit(void) -{ - if (power_suspend_kobj != NULL) - kobject_put(power_suspend_kobj); - - destroy_workqueue(suspend_work_queue); -} - -core_initcall(power_suspend_init); -module_exit(power_suspend_exit); - -MODULE_AUTHOR("Paul Reioux / Jean-Pierre Rasquin "); -MODULE_DESCRIPTION("power_suspend - A replacement kernel PM driver for" - "Android's deprecated early_suspend/late_resume PM driver!"); -MODULE_LICENSE("GPL v2"); - From 266349542d3e33a6adc49e5d6430b09b1663f604 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:04:14 +0100 Subject: [PATCH 077/296] Revert "power: quickwakeup: initial driver" This reverts commit a7c0792e63be6562c42ba63902a1793f1b917ffe. Signed-off-by: anupritaisno1 --- include/linux/quickwakeup.h | 46 ---------------- kernel/power/Kconfig | 8 --- kernel/power/Makefile | 1 - kernel/power/quickwakeup.c | 104 ------------------------------------ 4 files changed, 159 deletions(-) delete mode 100644 include/linux/quickwakeup.h delete mode 100644 kernel/power/quickwakeup.c diff --git a/include/linux/quickwakeup.h b/include/linux/quickwakeup.h deleted file mode 100644 index 000effa00763c..0000000000000 --- a/include/linux/quickwakeup.h +++ /dev/null @@ -1,46 +0,0 @@ -/* include/linux/quickwakeup.h - * - * Copyright (C) 2014 Motorola Mobility LLC. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 _QUICKWAKEUP_H_ -#define _QUICKWAKEUP_H_ - -#ifdef __KERNEL__ - -struct quickwakeup_ops { - struct list_head list; - char *name; - int (*qw_execute)(void *data); - int (*qw_check)(void *data); - int execute; - void *data; /* arbitrary data passed back to user */ -}; - -#ifdef CONFIG_QUICK_WAKEUP - -int quickwakeup_register(struct quickwakeup_ops *ops); -void quickwakeup_unregister(struct quickwakeup_ops *ops); -bool quickwakeup_suspend_again(void); - -#else - -static inline int quickwakeup_register(struct quickwakeup_ops *ops) { return 0; }; -static inline void quickwakeup_unregister(struct quickwakeup_ops *ops) {}; -static inline bool quickwakeup_suspend_again(void) { return 0; }; - -#endif /* CONFIG_QUICK_WAKEUP */ - -#endif /* __KERNEL__ */ - -#endif /* _QUICKWAKEUP_H_ */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 1299996387a38..47d945fc4b630 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -313,11 +313,3 @@ config PM_SYNC_BEFORE_SUSPEND bool "Sync file systems before suspend" depends on SUSPEND default y - -config QUICK_WAKEUP - bool "Quick wakeup" - depends on SUSPEND - default y - ---help--- - Allow kernel driver to do periodic jobs without resuming the full system - This option can increase battery life on android powered smartphone. diff --git a/kernel/power/Makefile b/kernel/power/Makefile index fdca558be0a35..74c713ba61b07 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o -obj-$(CONFIG_QUICK_WAKEUP) += quickwakeup.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o obj-$(CONFIG_SUSPEND) += wakeup_reason.o diff --git a/kernel/power/quickwakeup.c b/kernel/power/quickwakeup.c deleted file mode 100644 index 46f9dda7a3cb9..0000000000000 --- a/kernel/power/quickwakeup.c +++ /dev/null @@ -1,104 +0,0 @@ -/* kernel/power/quickwakeup.c - * - * Copyright (C) 2014 Motorola Mobility LLC. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 - -static LIST_HEAD(qw_head); -static DEFINE_MUTEX(list_lock); - -int quickwakeup_register(struct quickwakeup_ops *ops) -{ - mutex_lock(&list_lock); - list_add(&ops->list, &qw_head); - mutex_unlock(&list_lock); - - return 0; -} - -void quickwakeup_unregister(struct quickwakeup_ops *ops) -{ - mutex_lock(&list_lock); - list_del(&ops->list); - mutex_unlock(&list_lock); -} - -static int quickwakeup_check(void) -{ - int check = 0; - struct quickwakeup_ops *index; - - mutex_lock(&list_lock); - - list_for_each_entry(index, &qw_head, list) { - int ret = index->qw_check(index->data); - index->execute = ret; - check |= ret; - pr_debug("%s: %s votes for %s\n", __func__, index->name, - ret ? "execute" : "dont care"); - } - - mutex_unlock(&list_lock); - - return check; -} - -/* return 1 => suspend again - return 0 => continue wakeup - */ -static int quickwakeup_execute(void) -{ - int suspend_again = 0; - int final_vote = 1; - struct quickwakeup_ops *index; - - mutex_lock(&list_lock); - - list_for_each_entry(index, &qw_head, list) { - if (index->execute) { - int ret = index->qw_execute(index->data); - index->execute = 0; - final_vote &= ret; - suspend_again = final_vote; - pr_debug("%s: %s votes for %s\n", __func__, index->name, - ret ? "suspend again" : "wakeup"); - } - } - - mutex_unlock(&list_lock); - - pr_debug("%s: %s\n", __func__, - suspend_again ? "suspend again" : "wakeup"); - - return suspend_again; -} - -/* return 1 => suspend again - return 0 => continue wakeup - */ -bool quickwakeup_suspend_again(void) -{ - int ret = 0; - - if (quickwakeup_check()) - ret = quickwakeup_execute(); - - pr_debug("%s- returning %d %s\n", __func__, ret, - ret ? "suspend again" : "wakeup"); - - return ret; -} From 448f08e5464fb7b0ffd77450a29de67e9243b921 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:13:30 +0100 Subject: [PATCH 078/296] defconfig: cleanup junk values Signed-off-by: anupritaisno1 --- arch/arm64/configs/haruhi_defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/configs/haruhi_defconfig b/arch/arm64/configs/haruhi_defconfig index 13034144f363d..f4bc375aea3ae 100644 --- a/arch/arm64/configs/haruhi_defconfig +++ b/arch/arm64/configs/haruhi_defconfig @@ -602,8 +602,6 @@ CONFIG_FB_MSM_MDSS_KCAL_CTRL=y CONFIG_CRYPTO_SHA256_ARM64=y CONFIG_CRYPTO_SHA512_ARM64=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y -CONFIG_QUICK_WAKEUP=y -CONFIG_POWERSUSPEND=y # CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set # CONFIG_TCP_CONG_BIC is not set # CONFIG_TCP_CONG_CUBIC is not set From a4e8a5f0afbba7c05f93c3fe5e705eb5588d89d9 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:27:10 +0100 Subject: [PATCH 079/296] Revert "cpuidle: kick all cpu after idle handler installed" This reverts commit de9a72bd567d2cad6b92b4756b00149df9329ecb. Signed-off-by: anupritaisno1 --- drivers/cpuidle/cpuidle.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 155a1d3518934..c32a764dbbbbf 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -169,7 +169,6 @@ void cpuidle_install_idle_handler(void) /* Make sure all changes finished before we switch to new idle */ smp_wmb(); initialized = 1; - kick_all_cpus_sync(); } } From c78cdd3f46802091165832cf98246b627d9f5edd Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:07 +0100 Subject: [PATCH 080/296] Revert "drivers: cpuidle: Remove counting of last_residency" This reverts commit 6aba7d204d0a61e778f060fee6f8187863e01e61. Signed-off-by: anupritaisno1 --- drivers/cpuidle/cpuidle.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index c32a764dbbbbf..41bfa06e1902d 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -77,11 +77,23 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, int entered_state; struct cpuidle_state *target_state = &drv->states[index]; + ktime_t time_start, time_end; + s64 diff; + + time_start = ktime_get(); entered_state = target_state->enter(dev, drv, index); + time_end = ktime_get(); + local_irq_enable(); + diff = ktime_to_us(ktime_sub(time_end, time_start)); + if (diff > INT_MAX) + diff = INT_MAX; + + dev->last_residency = (int) diff; + if (entered_state >= 0) { /* Update cpuidle counters */ /* This can be moved to within driver enter routine From 26b7e5a1848261be52689069ad51504efd756a69 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:08 +0100 Subject: [PATCH 081/296] Revert "cpuidle: simplify multiple driver support" This reverts commit 9457c06acdbf1ee13a7ac905ba4b003a0dcbdff8. Signed-off-by: anupritaisno1 --- drivers/cpuidle/cpuidle.c | 4 +- drivers/cpuidle/driver.c | 192 ++++++++++++++++++++++---------------- include/linux/cpuidle.h | 6 +- 3 files changed, 118 insertions(+), 84 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 41bfa06e1902d..2eceafe8a2c40 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -469,7 +469,7 @@ void cpuidle_unregister(struct cpuidle_driver *drv) int cpu; struct cpuidle_device *device; - for_each_cpu(cpu, drv->cpumask) { + for_each_possible_cpu(cpu) { device = &per_cpu(cpuidle_dev, cpu); cpuidle_unregister_device(device); } @@ -501,7 +501,7 @@ int cpuidle_register(struct cpuidle_driver *drv, return ret; } - for_each_cpu(cpu, drv->cpumask) { + for_each_possible_cpu(cpu) { device = &per_cpu(cpuidle_dev, cpu); device->cpu = cpu; diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 95f3dc6747321..2253271b3fac3 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -18,140 +18,167 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); -#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS - -static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); +static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); +static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); -static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +static void cpuidle_setup_broadcast_timer(void *arg) { - return per_cpu(cpuidle_drivers, cpu); + int cpu = smp_processor_id(); + clockevents_notify((long)(arg), &cpu); } -static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) +static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) { - int cpu; + int i; + + drv->refcnt = 0; - for_each_cpu(cpu, drv->cpumask) { + for (i = drv->state_count - 1; i >= 0 ; i--) { - if (drv != __cpuidle_get_cpu_driver(cpu)) + if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) continue; - per_cpu(cpuidle_drivers, cpu) = NULL; + drv->bctimer = 1; + on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); + break; } } -static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) +static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) { - int cpu; + if (!drv || !drv->state_count) + return -EINVAL; - for_each_cpu(cpu, drv->cpumask) { + if (cpuidle_disabled()) + return -ENODEV; - if (__cpuidle_get_cpu_driver(cpu)) { - __cpuidle_unset_driver(drv); - return -EBUSY; - } + if (__cpuidle_get_cpu_driver(cpu)) + return -EBUSY; - per_cpu(cpuidle_drivers, cpu) = drv; - } + __cpuidle_driver_init(drv, cpu); + + __cpuidle_set_cpu_driver(drv, cpu); return 0; } -#else +static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) +{ + if (drv != __cpuidle_get_cpu_driver(cpu)) + return; -static struct cpuidle_driver *cpuidle_curr_driver; + if (!WARN_ON(drv->refcnt > 0)) + __cpuidle_set_cpu_driver(NULL, cpu); -static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) -{ - return cpuidle_curr_driver; + if (drv->bctimer) { + drv->bctimer = 0; + on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); + } } -static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) -{ - if (cpuidle_curr_driver) - return -EBUSY; +#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS - cpuidle_curr_driver = drv; +static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); - return 0; +static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) +{ + per_cpu(cpuidle_drivers, cpu) = drv; } -static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) +static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) { - if (drv == cpuidle_curr_driver) - cpuidle_curr_driver = NULL; + return per_cpu(cpuidle_drivers, cpu); } -#endif - -static void cpuidle_setup_broadcast_timer(void *arg) +static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) { - int cpu = smp_processor_id(); - clockevents_notify((long)(arg), &cpu); + int cpu; + for_each_present_cpu(cpu) + __cpuidle_unregister_driver(drv, cpu); } -static int __cpuidle_driver_init(struct cpuidle_driver *drv) +static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) { - int i; + int ret = 0; + int i, cpu; - drv->refcnt = 0; - - if (!drv->cpumask) - drv->cpumask = (struct cpumask *)cpu_possible_mask; - - for (i = drv->state_count - 1; i >= 0 ; i--) { + for_each_present_cpu(cpu) { + ret = __cpuidle_register_driver(drv, cpu); + if (ret) + break; + } - if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) - continue; + if (ret) + for_each_present_cpu(i) { + if (i == cpu) + break; + __cpuidle_unregister_driver(drv, i); + } - drv->bctimer = 1; - break; - } - return 0; + return ret; } -static int __cpuidle_register_driver(struct cpuidle_driver *drv) +int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) { int ret; - if (!drv || !drv->state_count) - return -EINVAL; + spin_lock(&cpuidle_driver_lock); + ret = __cpuidle_register_driver(drv, cpu); + spin_unlock(&cpuidle_driver_lock); - if (cpuidle_disabled()) - return -ENODEV; + return ret; +} - ret = __cpuidle_driver_init(drv); - if (ret) - return ret; +void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) +{ + spin_lock(&cpuidle_driver_lock); + __cpuidle_unregister_driver(drv, cpu); + spin_unlock(&cpuidle_driver_lock); +} - ret = __cpuidle_set_driver(drv); - if (ret) - return ret; +/** + * cpuidle_register_driver - registers a driver + * @drv: the driver + */ +int cpuidle_register_driver(struct cpuidle_driver *drv) +{ + int ret; - if (drv->bctimer) - on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); + spin_lock(&cpuidle_driver_lock); + ret = __cpuidle_register_all_cpu_driver(drv); + spin_unlock(&cpuidle_driver_lock); - return 0; + return ret; } +EXPORT_SYMBOL_GPL(cpuidle_register_driver); /** * cpuidle_unregister_driver - unregisters a driver * @drv: the driver */ -static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) +void cpuidle_unregister_driver(struct cpuidle_driver *drv) { - if (WARN_ON(drv->refcnt > 0)) - return; + spin_lock(&cpuidle_driver_lock); + __cpuidle_unregister_all_cpu_driver(drv); + spin_unlock(&cpuidle_driver_lock); +} +EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); - if (drv->bctimer) { - drv->bctimer = 0; - on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); - } +#else + +static struct cpuidle_driver *cpuidle_curr_driver; - __cpuidle_unset_driver(drv); +static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) +{ + cpuidle_curr_driver = drv; +} + +static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +{ + return cpuidle_curr_driver; } /** @@ -160,11 +187,13 @@ static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) */ int cpuidle_register_driver(struct cpuidle_driver *drv) { - int ret; + int ret, cpu; + cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv); + ret = __cpuidle_register_driver(drv, cpu); spin_unlock(&cpuidle_driver_lock); + put_cpu(); return ret; } @@ -176,11 +205,16 @@ EXPORT_SYMBOL_GPL(cpuidle_register_driver); */ void cpuidle_unregister_driver(struct cpuidle_driver *drv) { + int cpu; + + cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv); + __cpuidle_unregister_driver(drv, cpu); spin_unlock(&cpuidle_driver_lock); + put_cpu(); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); +#endif /** * cpuidle_get_driver - return the current driver diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 134e83d235da7..6282f8d88ebc3 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -112,9 +112,6 @@ struct cpuidle_driver { struct cpuidle_state states[CPUIDLE_STATE_MAX]; int state_count; int safe_state_index; - - /* the driver handles the cpus in cpumask */ - struct cpumask *cpumask; }; #ifdef CONFIG_CPU_IDLE @@ -139,6 +136,9 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); +extern int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu); +extern void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu); + #else static inline void disable_cpuidle(void) { } static inline int cpuidle_idle_call(void) { return -ENODEV; } From d661c5bc673fddee4e06427732aa440bc78cf9bd Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:09 +0100 Subject: [PATCH 082/296] Revert "lpm_levels: Avoid race by preventing hotplug during lpm probe" This reverts commit 84c0ad7c97043768299f4f6a6ac2652afa7b7798. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index ccf8acda592f8..5162e6ae54dea 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -979,12 +979,10 @@ static int lpm_probe(struct platform_device *pdev) int size; struct kobject *module_kobj = NULL; - get_online_cpus(); lpm_root_node = lpm_of_parse_cluster(pdev); if (IS_ERR_OR_NULL(lpm_root_node)) { pr_err("%s(): Failed to probe low power modes\n", __func__); - put_online_cpus(); return PTR_ERR(lpm_root_node); } @@ -997,6 +995,7 @@ static int lpm_probe(struct platform_device *pdev) * core. BUG in existing code but no known issues possibly because of * how late lpm_levels gets initialized. */ + register_hotcpu_notifier(&lpm_cpu_nblk); get_cpu(); on_each_cpu(setup_broadcast_timer, (void *)true, 1); put_cpu(); @@ -1007,7 +1006,6 @@ static int lpm_probe(struct platform_device *pdev) if (ret) { pr_err("%s: Failed initializing scm_handoff_lock (%d)\n", __func__, ret); - put_online_cpus(); return ret; } @@ -1017,13 +1015,12 @@ static int lpm_probe(struct platform_device *pdev) register_cluster_lpm_stats(lpm_root_node, NULL); ret = cluster_cpuidle_register(lpm_root_node); - put_online_cpus(); if (ret) { pr_err("%s()Failed to register with cpuidle framework\n", __func__); goto failed; } - register_hotcpu_notifier(&lpm_cpu_nblk); + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); if (!module_kobj) { pr_err("%s: cannot find kobject for module %s\n", From 5d084f438f7fcab10fe1519533c9d81e5dc40ac0 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:10 +0100 Subject: [PATCH 083/296] Revert "cpuidle: lpm_levels: Remove duplicate cpuidle tracepoints" This reverts commit f1fe6b3528a2659980a7f5e75e24eff894049fbc. Signed-off-by: anupritaisno1 --- 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 5162e6ae54dea..4c7b2b305a7fa 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -692,6 +692,7 @@ static int lpm_cpuidle_select(struct cpuidle_driver *drv, if (idx < 0) return -EPERM; + trace_cpu_idle_rcuidle(idx, dev->cpu); return idx; } @@ -734,6 +735,8 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, sched_set_cpu_cstate(smp_processor_id(), 0, &drv->states[0], 0, 0); trace_cpu_idle_exit(idx, success); + + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); end_time = ktime_to_ns(ktime_get()) - start_time; dev->last_residency = do_div(end_time, 1000); local_irq_enable(); From 11a3f5ebb30bfcbd33c29aaf644ed45a9a882a9a Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:11 +0100 Subject: [PATCH 084/296] Revert "cpuidle: lpm-levels: Remove local_irq_enable from lpm_cpuidle_select" This reverts commit daf8e997ed8b9da8fa788000e858633313c23f83. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 4c7b2b305a7fa..c497a0aafd78b 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -689,8 +689,10 @@ static int lpm_cpuidle_select(struct cpuidle_driver *drv, idx = cpu_power_select(dev, cluster->cpu); - if (idx < 0) + if (idx < 0) { + local_irq_enable(); return -EPERM; + } trace_cpu_idle_rcuidle(idx, dev->cpu); return idx; From 12e8d412fc8b26c1d9adba0189273a8c1c6a5072 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:11 +0100 Subject: [PATCH 085/296] Revert "lpm-levels: Check for need_resched just before entering PSCI" This reverts commit b9d68704148fd7f12d655979b8f0549722090b6a. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index c497a0aafd78b..3c66422b77deb 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -707,6 +707,11 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, int64_t start_time = ktime_to_ns(ktime_get()), end_time; struct power_params *pwr_params; + if (need_resched()) { + dev->last_residency = 0; + goto exit; + } + pwr_params = &cluster->cpu->levels[idx].pwr; sched_set_cpu_cstate(smp_processor_id(), idx + 1, &drv->states[idx+1], pwr_params->energy_overhead, pwr_params->latency_us); @@ -718,9 +723,6 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, trace_cpu_idle_enter(idx); lpm_stats_cpu_enter(idx); - if (need_resched() || (idx < 0)) - goto exit; - if (idx > 0) update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed, 0xdeaffeed, true); @@ -730,7 +732,6 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, update_debug_pc_event(CPU_EXIT, idx, success, 0xdeaffeed, true); -exit: lpm_stats_cpu_exit(idx, success); cluster_unprepare(cluster, cpumask, idx, true); cpu_unprepare(cluster, idx, true); @@ -738,6 +739,7 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, sched_set_cpu_cstate(smp_processor_id(), 0, &drv->states[0], 0, 0); trace_cpu_idle_exit(idx, success); +exit: trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); end_time = ktime_to_ns(ktime_get()) - start_time; dev->last_residency = do_div(end_time, 1000); From c3c4a2bf66e00eee747b837fb95ad63816b09a6b Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:12 +0100 Subject: [PATCH 086/296] Revert "lpm-levels: Add QCOM cpuidle governor" This reverts commit 8e52aa999df738052025967af28b9aff5c2d282b. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 59 +++++++++--------------------------- 1 file changed, 14 insertions(+), 45 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 3c66422b77deb..c1b1c52776563 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -273,7 +273,7 @@ int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm) } static int cpu_power_select(struct cpuidle_device *dev, - struct lpm_cpu *cpu) + struct lpm_cpu *cpu, int *index) { int best_level = -1; uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY, @@ -678,16 +678,15 @@ static inline void cpu_unprepare(struct lpm_cluster *cluster, int cpu_index, cpu_pm_exit(); } -static int lpm_cpuidle_select(struct cpuidle_driver *drv, - struct cpuidle_device *dev) +static int lpm_cpuidle_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu); - int idx; - - if (!cluster) - return 0; - - idx = cpu_power_select(dev, cluster->cpu); + int64_t time = ktime_to_ns(ktime_get()); + bool success = true; + int idx = cpu_power_select(dev, cluster->cpu, &index); + const struct cpumask *cpumask = get_cpu_mask(dev->cpu); + struct power_params *pwr_params; if (idx < 0) { local_irq_enable(); @@ -695,17 +694,6 @@ static int lpm_cpuidle_select(struct cpuidle_driver *drv, } trace_cpu_idle_rcuidle(idx, dev->cpu); - return idx; -} - -static int lpm_cpuidle_enter(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int idx) -{ - struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu); - bool success = true; - const struct cpumask *cpumask = get_cpu_mask(dev->cpu); - int64_t start_time = ktime_to_ns(ktime_get()), end_time; - struct power_params *pwr_params; if (need_resched()) { dev->last_residency = 0; @@ -716,13 +704,11 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, sched_set_cpu_cstate(smp_processor_id(), idx + 1, &drv->states[idx+1], pwr_params->energy_overhead, pwr_params->latency_us); + trace_cpu_idle_enter(idx); cpu_prepare(cluster, idx, true); cluster_prepare(cluster, cpumask, idx, true); - - trace_cpu_idle_enter(idx); lpm_stats_cpu_enter(idx); - if (idx > 0) update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed, 0xdeaffeed, true); @@ -731,20 +717,20 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, if (idx > 0) update_debug_pc_event(CPU_EXIT, idx, success, 0xdeaffeed, true); - lpm_stats_cpu_exit(idx, success); cluster_unprepare(cluster, cpumask, idx, true); cpu_unprepare(cluster, idx, true); sched_set_cpu_cstate(smp_processor_id(), 0, &drv->states[0], 0, 0); + + time = ktime_to_ns(ktime_get()) - time; + do_div(time, 1000); + dev->last_residency = (int)time; trace_cpu_idle_exit(idx, success); exit: trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); - end_time = ktime_to_ns(ktime_get()) - start_time; - dev->last_residency = do_div(end_time, 1000); local_irq_enable(); - return idx; } @@ -791,13 +777,6 @@ static int cpuidle_register_cpu(struct cpuidle_driver *drv, } #endif -static struct cpuidle_governor lpm_governor = { - .name = "qcom", - .rating = 30, - .select = lpm_cpuidle_select, - .owner = THIS_MODULE, -}; - static int cluster_cpuidle_register(struct lpm_cluster *cl) { int i = 0, ret = 0; @@ -859,19 +838,10 @@ static int cluster_cpuidle_register(struct lpm_cluster *cl) kfree(cl->drv); return -ENOMEM; } - return 0; -} -/** - * init_lpm - initializes the governor - */ -static int __init init_lpm(void) -{ - return cpuidle_register_governor(&lpm_governor); + return 0; } -postcore_initcall(init_lpm); - static void register_cpu_lpm_stats(struct lpm_cpu *cpu, struct lpm_cluster *parent) { @@ -1182,4 +1152,3 @@ void lpm_cpu_hotplug_enter(unsigned int cpu) msm_cpu_pm_enter_sleep(mode, false); } - From aaf2fc7e15d74ea5bed5f197f22229a5b1df2c0e Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:13 +0100 Subject: [PATCH 087/296] Revert "lpm-levels: Fix driver registration after API change" This reverts commit afbc101b7aaf12adf517b5cb97a169923b0ed5f9. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index c1b1c52776563..6e67ed73839fb 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -745,14 +745,12 @@ static int cpuidle_register_cpu(struct cpuidle_driver *drv, if (!mask || !drv) return -EINVAL; - drv->cpumask = mask; - ret = cpuidle_register_driver(drv); - if (ret) { - pr_err("Failed to register cpuidle driver %d\n", ret); - goto failed_driver_register; - } - for_each_cpu(cpu, mask) { + ret = cpuidle_register_cpu_driver(drv, cpu); + if (ret) { + pr_err("Failed to register cpuidle driver %d\n", ret); + goto failed_driver_register; + } device = &per_cpu(cpuidle_dev, cpu); device->cpu = cpu; @@ -766,7 +764,7 @@ static int cpuidle_register_cpu(struct cpuidle_driver *drv, return ret; failed_driver_register: for_each_cpu(cpu, mask) - cpuidle_unregister_driver(drv); + cpuidle_unregister_cpu_driver(drv, cpu); return ret; } #else From 8bc24879156820f7aa4c11e99c4fa1d271f27087 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:14 +0100 Subject: [PATCH 088/296] Revert "lpm-levels: Use residency instead of power and energy overhead values" This reverts commit e7a24616b45ec7b4111611b5c65250d5f44660d0. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels-of.c | 160 +++----------------------------- drivers/cpuidle/lpm-levels.c | 47 ++++++++-- drivers/cpuidle/lpm-levels.h | 7 +- 3 files changed, 54 insertions(+), 160 deletions(-) diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index bedef3098441d..a4995e2864b23 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -37,87 +37,25 @@ static const struct lpm_type_str lpm_types[] = { {SUSPEND, "suspend_enabled"}, }; -static DEFINE_PER_CPU(uint32_t *, max_residency); static struct lpm_level_avail *cpu_level_available[NR_CPUS]; static struct platform_device *lpm_pdev; -static void *get_enabled_ptr(struct kobj_attribute *attr, - struct lpm_level_avail *avail) +static void *get_avail_val(struct kobject *kobj, struct kobj_attribute *attr) { void *arg = NULL; - - if (!strcmp(attr->attr.name, lpm_types[IDLE].str)) - arg = (void *) &avail->idle_enabled; - else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) - arg = (void *) &avail->suspend_enabled; - - return arg; -} - -static struct lpm_level_avail *get_avail_ptr(struct kobject *kobj, - struct kobj_attribute *attr) -{ struct lpm_level_avail *avail = NULL; - if (!strcmp(attr->attr.name, lpm_types[IDLE].str)) + if (!strcmp(attr->attr.name, lpm_types[IDLE].str)) { avail = container_of(attr, struct lpm_level_avail, idle_enabled_attr); - else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) + arg = (void *) &avail->idle_enabled; + } else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) { avail = container_of(attr, struct lpm_level_avail, suspend_enabled_attr); - - return avail; -} - -static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id, - bool probe_time) -{ - int i, j; - bool mode_avail; - uint32_t *residency = per_cpu(max_residency, cpu_id); - - for (i = 0; i < cpu->nlevels; i++) { - struct power_params *pwr = &cpu->levels[i].pwr; - - residency[i] = ~0; - for (j = i + 1; j < cpu->nlevels; j++) { - mode_avail = probe_time || - lpm_cpu_mode_allow(cpu_id, j, true); - - if (mode_avail && - (residency[i] > pwr->residencies[j]) && - (pwr->residencies[j] != 0)) - residency[i] = pwr->residencies[j]; - } - } -} - -static void set_optimum_cluster_residency(struct lpm_cluster *cluster, - bool probe_time) -{ - int i, j; - bool mode_avail = false; - - for (i = 0; i < cluster->nlevels; i++) { - struct power_params *pwr = &cluster->levels[i].pwr; - - pwr->max_residency = ~0; - for (j = 0; j < cluster->nlevels; j++) { - if (i >= j) - mode_avail = probe_time || - lpm_cluster_mode_allow(cluster, i, - true); - if (mode_avail && - (pwr->max_residency > pwr->residencies[j]) && - (pwr->residencies[j] != 0)) - pwr->max_residency = pwr->residencies[j]; - } + arg = (void *) &avail->suspend_enabled; } -} -uint32_t *get_per_cpu_max_residency(int cpu) -{ - return per_cpu(max_residency, cpu); + return arg; } ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr, @@ -126,7 +64,7 @@ ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr, int ret = 0; struct kernel_param kp; - kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr)); + kp.arg = get_avail_val(kobj, attr); ret = param_get_bool(buf, &kp); if (ret > 0) { strlcat(buf, "\n", PAGE_SIZE); @@ -141,23 +79,15 @@ ssize_t lpm_enable_store(struct kobject *kobj, struct kobj_attribute *attr, { int ret = 0; struct kernel_param kp; - struct lpm_level_avail *avail; - avail = get_avail_ptr(kobj, attr); - kp.arg = get_enabled_ptr(attr, avail); + kp.arg = get_avail_val(kobj, attr); ret = param_set_bool(buf, &kp); - if (avail->cpu_node) - set_optimum_cpu_residency(avail->data, avail->idx, false); - else - set_optimum_cluster_residency(avail->data, false); - return ret ? ret : len; } static int create_lvl_avail_nodes(const char *name, - struct kobject *parent, struct lpm_level_avail *avail, - void *data, int index, bool cpu_node) + struct kobject *parent, struct lpm_level_avail *avail) { struct attribute_group *attr_group = NULL; struct attribute **attr = NULL; @@ -208,9 +138,6 @@ static int create_lvl_avail_nodes(const char *name, avail->idle_enabled = true; avail->suspend_enabled = true; avail->kobj = kobj; - avail->data = data; - avail->idx = index; - avail->cpu_node = cpu_node; return ret; @@ -262,8 +189,7 @@ static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent) } ret = create_lvl_avail_nodes(p->cpu->levels[j].name, - cpu_kobj[cpu_idx], &level_list[i], - (void *)p->cpu, cpu, true); + cpu_kobj[cpu_idx], &level_list[i]); if (ret) goto release_kobj; } @@ -298,8 +224,7 @@ int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj) for (i = 0; i < p->nlevels; i++) { ret = create_lvl_avail_nodes(p->levels[i].level_name, - cluster_kobj, &p->levels[i].available, - (void *)p, 0, false); + cluster_kobj, &p->levels[i].available); if (ret) return ret; } @@ -468,9 +393,6 @@ static int parse_power_params(struct device_node *node, key = "qcom,time-overhead"; ret = of_property_read_u32(node, key, &pwr->time_overhead_us); - if (ret) - goto fail; - fail: if (ret) pr_err("%s(): %s Error reading %s\n", __func__, node->name, @@ -606,31 +528,11 @@ static int get_cpumask_for_node(struct device_node *node, struct cpumask *mask) return -EINVAL; } -static int calculate_residency(struct power_params *base_pwr, - struct power_params *next_pwr) -{ - int32_t residency = (int32_t)(next_pwr->energy_overhead - - base_pwr->energy_overhead) - - ((int32_t)(next_pwr->ss_power * next_pwr->time_overhead_us) - - (int32_t)(base_pwr->ss_power * base_pwr->time_overhead_us)); - - residency /= (int32_t)(base_pwr->ss_power - next_pwr->ss_power); - - if (residency < 0) { - __WARN_printf("%s: Incorrect power attributes for LPM\n", - __func__); - return next_pwr->time_overhead_us; - } - - return residency < next_pwr->time_overhead_us ? - next_pwr->time_overhead_us : residency; -} - static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) { struct device_node *n; int ret = -ENOMEM; - int i, j; + int i; c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL); if (!c->cpu) @@ -664,22 +566,6 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) l->use_bc_timer = of_property_read_bool(n, key); } - for (i = 0; i < c->cpu->nlevels; i++) { - for (j = 0; j < c->cpu->nlevels; j++) { - if (i >= j) { - c->cpu->levels[i].pwr.residencies[j] = 0; - continue; - } - - c->cpu->levels[i].pwr.residencies[j] = - calculate_residency(&c->cpu->levels[i].pwr, - &c->cpu->levels[j].pwr); - - pr_err("%s: idx %d %u\n", __func__, j, - c->cpu->levels[i].pwr.residencies[j]); - } - } - return 0; failed: for (i = 0; i < c->cpu->nlevels; i++) { @@ -736,7 +622,6 @@ struct lpm_cluster *parse_cluster(struct device_node *node, struct device_node *n; char *key; int ret = 0; - int i, j; c = devm_kzalloc(&lpm_pdev->dev, sizeof(*c), GFP_KERNEL); if (!c) @@ -794,32 +679,11 @@ struct lpm_cluster *parse_cluster(struct device_node *node, goto failed_parse_cluster; c->aff_level = 1; - - for_each_cpu(i, &c->child_cpus) { - per_cpu(max_residency, i) = devm_kzalloc( - &lpm_pdev->dev, - sizeof(uint32_t) * c->cpu->nlevels, - GFP_KERNEL); - if (!per_cpu(max_residency, i)) - return ERR_PTR(-ENOMEM); - set_optimum_cpu_residency(c->cpu, i, true); - } } } c->last_level = c->nlevels-1; - for (i = 0; i < c->nlevels; i++) { - for (j = 0; j < c->nlevels; j++) { - if (i >= j) { - c->levels[i].pwr.residencies[j] = 0; - continue; - } - c->levels[i].pwr.residencies[j] = calculate_residency( - &c->levels[i].pwr, &c->levels[j].pwr); - } - } - set_optimum_cluster_residency(c, true); return c; failed_parse_cluster: diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 6e67ed73839fb..62fcb680386e8 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -276,15 +276,18 @@ static int cpu_power_select(struct cpuidle_device *dev, struct lpm_cpu *cpu, int *index) { int best_level = -1; + uint32_t best_level_pwr = ~0U; uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY, dev->cpu); uint32_t sleep_us = (uint32_t)(ktime_to_us(tick_nohz_get_sleep_length())); uint32_t modified_time_us = 0; uint32_t next_event_us = 0; + uint32_t pwr; int i; uint32_t lvl_latency_us = 0; - uint32_t *residency = get_per_cpu_max_residency(dev->cpu); + uint32_t lvl_overhead_us = 0; + uint32_t lvl_overhead_energy = 0; if (!cpu) return -EINVAL; @@ -308,11 +311,15 @@ static int cpu_power_select(struct cpuidle_device *dev, lvl_latency_us = pwr_params->latency_us; + lvl_overhead_us = pwr_params->time_overhead_us; + + lvl_overhead_energy = pwr_params->energy_overhead; + if (i > 0 && suspend_in_progress) continue; if (latency_us < lvl_latency_us) - break; + continue; if (next_event_us) { if (next_event_us < lvl_latency_us) @@ -323,15 +330,32 @@ static int cpu_power_select(struct cpuidle_device *dev, next_wakeup_us = next_event_us - lvl_latency_us; } - if (next_wakeup_us <= residency[i]) { + if (next_wakeup_us <= pwr_params->time_overhead_us) + continue; + + /* + * If wakeup time greater than overhead by a factor of 1000 + * assume that core steady state power dominates the power + * equation + */ + if ((next_wakeup_us >> 10) > lvl_overhead_us) { + pwr = pwr_params->ss_power; + } else { + pwr = pwr_params->ss_power; + pwr -= (lvl_overhead_us * pwr_params->ss_power) / + next_wakeup_us; + pwr += pwr_params->energy_overhead / next_wakeup_us; + } + + if (best_level_pwr >= pwr) { best_level = i; + best_level_pwr = pwr; if (next_event_us && next_event_us < sleep_us && (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)) modified_time_us = next_event_us - lvl_latency_us; else modified_time_us = 0; - break; } } @@ -389,6 +413,8 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle) { int best_level = -1; int i; + uint32_t best_level_pwr = ~0U; + uint32_t pwr; struct cpumask mask; uint32_t latency_us = ~0U; uint32_t sleep_us; @@ -440,9 +466,18 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle) if (level->notify_rpm && msm_rpm_waiting_for_ack()) continue; - if (sleep_us <= pwr_params->max_residency) { + if ((sleep_us >> 10) > pwr_params->time_overhead_us) { + pwr = pwr_params->ss_power; + } else { + pwr = pwr_params->ss_power; + pwr -= (pwr_params->time_overhead_us * + pwr_params->ss_power) / sleep_us; + pwr += pwr_params->energy_overhead / sleep_us; + } + + if (best_level_pwr >= pwr) { best_level = i; - break; + best_level_pwr = pwr; } } diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index 5a48fb2c94564..b48b6bbdb18f2 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -25,8 +25,6 @@ struct power_params { uint32_t ss_power; /* Steady state power */ uint32_t energy_overhead; /* Enter + exit over head */ uint32_t time_overhead_us; /* Enter + exit overhead */ - uint32_t residencies[NR_LPM_LEVELS]; - uint32_t max_residency; }; struct lpm_cpu_level { @@ -48,9 +46,6 @@ struct lpm_level_avail { struct kobject *kobj; struct kobj_attribute idle_enabled_attr; struct kobj_attribute suspend_enabled_attr; - void *data; - int idx; - bool cpu_node; }; struct lpm_cluster_level { @@ -110,7 +105,7 @@ bool lpm_cpu_mode_allow(unsigned int cpu, unsigned int mode, bool from_idle); bool lpm_cluster_mode_allow(struct lpm_cluster *cluster, unsigned int mode, bool from_idle); -uint32_t *get_per_cpu_max_residency(int cpu); + extern struct lpm_cluster *lpm_root_node; #ifdef CONFIG_SMP From a73e9f861aa1cd59fa7b2f8db178ae0fc3a0f9e8 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:15 +0100 Subject: [PATCH 089/296] Revert "msm: lpm-levels: Log CPU lpm enter exit events" This reverts commit 8ee5fc826567f9e80b6ada9d55546dab2dc8d99d. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 62fcb680386e8..dac2e735a331c 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -744,14 +744,7 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, cluster_prepare(cluster, cpumask, idx, true); lpm_stats_cpu_enter(idx); - if (idx > 0) - update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed, 0xdeaffeed, - true); - success = msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, - true); - if (idx > 0) - update_debug_pc_event(CPU_EXIT, idx, success, 0xdeaffeed, - true); + success = msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, true); lpm_stats_cpu_exit(idx, success); cluster_unprepare(cluster, cpumask, idx, true); cpu_unprepare(cluster, idx, true); @@ -963,13 +956,7 @@ static int lpm_suspend_enter(suspend_state_t state) } cpu_prepare(cluster, idx, false); cluster_prepare(cluster, cpumask, idx, false); - if (idx > 0) - update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed, - 0xdeaffeed, false); msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, false); - if (idx > 0) - update_debug_pc_event(CPU_EXIT, idx, true, 0xdeaffeed, - false); cluster_unprepare(cluster, cpumask, idx, false); cpu_unprepare(cluster, idx, false); return 0; From b134bf8d2e733a8fdcfbd590dd6201dc32cf2578 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:15 +0100 Subject: [PATCH 090/296] Revert "power: qcom: Select CPU_IDLE_MULTIPLE_DRIVERS" This reverts commit 45bc51655e50575e5dbd264bfa1cbb75f4b14f31. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 1 + drivers/power/qcom/Kconfig | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index dac2e735a331c..11af769bcdd81 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -763,6 +763,7 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, } #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS +static DEFINE_PER_CPU(struct cpuidle_device, cpuidle_dev); static int cpuidle_register_cpu(struct cpuidle_driver *drv, struct cpumask *mask) { diff --git a/drivers/power/qcom/Kconfig b/drivers/power/qcom/Kconfig index 70edaeea2026d..afc41e07b72d3 100644 --- a/drivers/power/qcom/Kconfig +++ b/drivers/power/qcom/Kconfig @@ -1,7 +1,6 @@ config MSM_PM depends on PM select MSM_IDLE_STATS if DEBUG_FS - select CPU_IDLE_MULTIPLE_DRIVERS bool "Qualcomm platform specific PM driver" help Platform specific power driver to manage cores and l2 From 1b2a03b0cd7cb1cf7d75f211c4f74ae80c1c96f5 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:16 +0100 Subject: [PATCH 091/296] Revert "msm: lpm-levels: add suspend_again handler" This reverts commit b86d7ef38850a802ef4b2d04b03e8845db5c2445. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 11af769bcdd81..ed8a3d0b0bb37 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -968,7 +967,6 @@ static const struct platform_suspend_ops lpm_suspend_ops = { .valid = suspend_valid_only_mem, .prepare_late = lpm_suspend_prepare, .end = lpm_suspend_end, - .suspend_again = quickwakeup_suspend_again, }; static int lpm_probe(struct platform_device *pdev) From d9bafc62ef9c36a26742f504978c1fc789ac2dff Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:17 +0100 Subject: [PATCH 092/296] Revert "drivers: cpuidle: msm: Use 64 bit type for sclk" This reverts commit 17178ee02f904eb49fcc0af1df4eb46a6b776ec2. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 4 ++-- drivers/soc/qcom/mpm-of.c | 2 +- include/linux/irqchip/msm-mpm-irq.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index ed8a3d0b0bb37..1454c20971390 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -523,7 +523,7 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, } if (level->notify_rpm) { struct cpumask nextcpu, *cpumask; - uint64_t us; + uint32_t us; us = get_cluster_sleep_time(cluster, &nextcpu, from_idle); cpumask = level->disable_dynamic_routing ? NULL : &nextcpu; @@ -535,7 +535,7 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, } do_div(us, USEC_PER_SEC/SCLK_HZ); - msm_mpm_enter_sleep(us, from_idle, cpumask); + msm_mpm_enter_sleep((uint32_t)us, from_idle, cpumask); } cluster->last_level = idx; spin_unlock(&cluster->sync_lock); diff --git a/drivers/soc/qcom/mpm-of.c b/drivers/soc/qcom/mpm-of.c index fa441639dd6cf..ea69ba60072cc 100644 --- a/drivers/soc/qcom/mpm-of.c +++ b/drivers/soc/qcom/mpm-of.c @@ -528,7 +528,7 @@ bool msm_mpm_irqs_detectable(bool from_idle) from_idle); } -void msm_mpm_enter_sleep(uint64_t sclk_count, bool from_idle, +void msm_mpm_enter_sleep(uint32_t sclk_count, bool from_idle, const struct cpumask *cpumask) { cycle_t wakeup = (u64)sclk_count * ARCH_TIMER_HZ; diff --git a/include/linux/irqchip/msm-mpm-irq.h b/include/linux/irqchip/msm-mpm-irq.h index d59444330b42d..7b8a6a4765607 100644 --- a/include/linux/irqchip/msm-mpm-irq.h +++ b/include/linux/irqchip/msm-mpm-irq.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2014, 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 @@ -98,7 +98,7 @@ bool msm_mpm_gpio_irqs_detectable(bool from_idle); * Low power management code calls into this API to configure the MPM to * monitor the active irqs before going to sleep. */ -void msm_mpm_enter_sleep(uint64_t sclk_count, bool from_idle, +void msm_mpm_enter_sleep(uint32_t sclk_count, bool from_idle, const struct cpumask *cpumask); /** * msm_mpm_exit_sleep() -Called from PM code after resuming from low power mode @@ -140,7 +140,7 @@ static inline bool msm_mpm_irqs_detectable(bool from_idle) { return false; } static inline bool msm_mpm_gpio_irqs_detectable(bool from_idle) { return false; } -static inline void msm_mpm_enter_sleep(uint64_t sclk_count, bool from_idle, +static inline void msm_mpm_enter_sleep(uint32_t sclk_count, bool from_idle, const struct cpumask *cpumask) {} static inline void msm_mpm_exit_sleep(bool from_idle) {} static inline void __init of_mpm_init(void) {} From 5b52f8fae88e6d9be1e2703bd6995739644a2a2f Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:18 +0100 Subject: [PATCH 093/296] Revert "drivers: lpm-levels: Don't allow idle low power modes during resume" This reverts commit b584e3d75503498a102965d101d4ea840a2dd92f. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 1454c20971390..451ca16bd4d9e 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -314,9 +314,6 @@ static int cpu_power_select(struct cpuidle_device *dev, lvl_overhead_energy = pwr_params->energy_overhead; - if (i > 0 && suspend_in_progress) - continue; - if (latency_us < lvl_latency_us) continue; @@ -929,7 +926,7 @@ static int lpm_suspend_prepare(void) return 0; } -static void lpm_suspend_end(void) +static void lpm_suspend_wake(void) { suspend_in_progress = false; msm_mpm_suspend_wake(); @@ -966,7 +963,7 @@ static const struct platform_suspend_ops lpm_suspend_ops = { .enter = lpm_suspend_enter, .valid = suspend_valid_only_mem, .prepare_late = lpm_suspend_prepare, - .end = lpm_suspend_end, + .wake = lpm_suspend_wake, }; static int lpm_probe(struct platform_device *pdev) From 7427a4ef296e2387759c774fefd2c16f6adb6cc6 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:19 +0100 Subject: [PATCH 094/296] Revert "cpuidle: lpm-levels: Rename cpumask variable name" This reverts commit 8628dc86d994bcf4c5e4d952887bd4e5601a1edb. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 37 ++++++++++++++++++------------------ drivers/cpuidle/lpm-levels.h | 2 +- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 451ca16bd4d9e..2b37c064f8a86 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -385,7 +385,7 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster, } cpumask_and(&online_cpus_in_cluster, - &cluster->num_children_in_sync, cpu_online_mask); + &cluster->num_childs_in_sync, cpu_online_mask); for_each_cpu(cpu, &online_cpus_in_cluster) { td = &per_cpu(tick_cpu_device, cpu); @@ -446,7 +446,7 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle) cpumask_weight(cpu_online_mask) > 1) continue; - if (!cpumask_equal(&cluster->num_children_in_sync, + if (!cpumask_equal(&cluster->num_childs_in_sync, &level->num_cpu_votes)) continue; @@ -488,18 +488,18 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, spin_lock(&cluster->sync_lock); - if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus) - || is_IPI_pending(&cluster->num_children_in_sync)) { + if (!cpumask_equal(&cluster->num_childs_in_sync, &cluster->child_cpus) + || is_IPI_pending(&cluster->num_childs_in_sync)) { spin_unlock(&cluster->sync_lock); return -EPERM; } if (idx != cluster->default_level) { update_debug_pc_event(CLUSTER_ENTER, idx, - cluster->num_children_in_sync.bits[0], + cluster->num_childs_in_sync.bits[0], cluster->child_cpus.bits[0], from_idle); trace_cluster_enter(cluster->cluster_name, idx, - cluster->num_children_in_sync.bits[0], + cluster->num_childs_in_sync.bits[0], cluster->child_cpus.bits[0], from_idle); lpm_stats_cluster_enter(cluster->stats, idx); } @@ -563,8 +563,8 @@ static void cluster_prepare(struct lpm_cluster *cluster, return; spin_lock(&cluster->sync_lock); - cpumask_or(&cluster->num_children_in_sync, cpu, - &cluster->num_children_in_sync); + cpumask_or(&cluster->num_childs_in_sync, cpu, + &cluster->num_childs_in_sync); for (i = 0; i < cluster->nlevels; i++) { struct lpm_cluster_level *lvl = &cluster->levels[i]; @@ -581,7 +581,7 @@ static void cluster_prepare(struct lpm_cluster *cluster, * configuration process */ - if (!cpumask_equal(&cluster->num_children_in_sync, + if (!cpumask_equal(&cluster->num_childs_in_sync, &cluster->child_cpus)) { spin_unlock(&cluster->sync_lock); return; @@ -596,7 +596,7 @@ static void cluster_prepare(struct lpm_cluster *cluster, if (cluster_configure(cluster, i, from_idle)) return; - cluster_prepare(cluster->parent, &cluster->num_children_in_sync, i, + cluster_prepare(cluster->parent, &cluster->num_childs_in_sync, i, from_idle); } @@ -615,10 +615,10 @@ static void cluster_unprepare(struct lpm_cluster *cluster, spin_lock(&cluster->sync_lock); last_level = cluster->default_level; - first_cpu = cpumask_equal(&cluster->num_children_in_sync, + first_cpu = cpumask_equal(&cluster->num_childs_in_sync, &cluster->child_cpus); - cpumask_andnot(&cluster->num_children_in_sync, - &cluster->num_children_in_sync, cpu); + cpumask_andnot(&cluster->num_childs_in_sync, + &cluster->num_childs_in_sync, cpu); for (i = 0; i < cluster->nlevels; i++) { struct lpm_cluster_level *lvl = &cluster->levels[i]; @@ -640,10 +640,10 @@ static void cluster_unprepare(struct lpm_cluster *cluster, } update_debug_pc_event(CLUSTER_EXIT, cluster->last_level, - cluster->num_children_in_sync.bits[0], + cluster->num_childs_in_sync.bits[0], cluster->child_cpus.bits[0], from_idle); trace_cluster_exit(cluster->cluster_name, cluster->last_level, - cluster->num_children_in_sync.bits[0], + cluster->num_childs_in_sync.bits[0], cluster->child_cpus.bits[0], from_idle); last_level = cluster->last_level; @@ -847,10 +847,10 @@ static int cluster_cpuidle_register(struct lpm_cluster *cl) while (p) { int j; spin_lock(&p->sync_lock); - cpumask_set_cpu(cpu, &p->num_children_in_sync); + cpumask_set_cpu(cpu, &p->num_childs_in_sync); for (j = 0; j < p->nlevels; j++) cpumask_copy(&p->levels[j].num_cpu_votes, - &p->num_children_in_sync); + &p->num_childs_in_sync); spin_unlock(&p->sync_lock); p = p->parent; } @@ -1091,8 +1091,7 @@ enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu) goto unlock_and_return; } - if (!cpumask_equal(&cluster->num_children_in_sync, - &cluster->child_cpus)) + if (!cpumask_equal(&cluster->num_childs_in_sync, &cluster->child_cpus)) goto unlock_and_return; if (cluster->lpm_dev) diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index b48b6bbdb18f2..17dbdd94737fe 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -85,7 +85,7 @@ struct lpm_cluster { struct cpuidle_driver *drv; spinlock_t sync_lock; struct cpumask child_cpus; - struct cpumask num_children_in_sync; + struct cpumask num_childs_in_sync; struct lpm_cluster *parent; struct lpm_stats *stats; bool no_saw_devices; From a44777ccd202108d62ebb74661f8f197e4c29bfe Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:19 +0100 Subject: [PATCH 095/296] Revert "msm: lpm-levels: Modify ftrace events to track latency/sleep time" This reverts commit 2c000b006e9a7908de4f4e50c57bbf77cd5dcb8b. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 7 +++---- include/trace/events/trace_msm_low_power.h | 21 +-------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 2b37c064f8a86..dad549cf189cb 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -358,7 +358,7 @@ static int cpu_power_select(struct cpuidle_device *dev, if (modified_time_us) msm_pm_set_timer(modified_time_us); - trace_cpu_power_select(best_level, sleep_us, latency_us, next_event_us); + trace_cpu_idle_enter(best_level, sleep_us, latency_us, next_event_us); return best_level; } @@ -735,13 +735,13 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, sched_set_cpu_cstate(smp_processor_id(), idx + 1, &drv->states[idx+1], pwr_params->energy_overhead, pwr_params->latency_us); - trace_cpu_idle_enter(idx); cpu_prepare(cluster, idx, true); cluster_prepare(cluster, cpumask, idx, true); lpm_stats_cpu_enter(idx); success = msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, true); lpm_stats_cpu_exit(idx, success); + trace_cpu_idle_exit(idx, success); cluster_unprepare(cluster, cpumask, idx, true); cpu_unprepare(cluster, idx, true); @@ -750,11 +750,10 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, time = ktime_to_ns(ktime_get()) - time; do_div(time, 1000); dev->last_residency = (int)time; - trace_cpu_idle_exit(idx, success); exit: - trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); local_irq_enable(); + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); return idx; } diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h index b06185fd57785..ac9127875a2dc 100644 --- a/include/trace/events/trace_msm_low_power.h +++ b/include/trace/events/trace_msm_low_power.h @@ -18,7 +18,7 @@ #include -TRACE_EVENT(cpu_power_select, +TRACE_EVENT(cpu_idle_enter, TP_PROTO(int index, u32 sleep_us, u32 latency, u32 next_event_us), @@ -42,25 +42,6 @@ TRACE_EVENT(cpu_power_select, __entry->index, __entry->sleep_us, __entry->latency, __entry->next_event_us) ); - -TRACE_EVENT(cpu_idle_enter, - - TP_PROTO(int index), - - TP_ARGS(index), - - TP_STRUCT__entry( - __field(int, index) - ), - - TP_fast_assign( - __entry->index = index; - ), - - TP_printk("idx:%d", - __entry->index) -); - TRACE_EVENT(cpu_idle_exit, TP_PROTO(int index, bool success), From 81d293ac815110f604ba29798fcfb38814eb2ccb Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:20 +0100 Subject: [PATCH 096/296] Revert "drivers: lpm-levels: Update cpu mask to get cluster sleep time" This reverts commit 33be8c79b234ccd01c4a663554da3ca8ae529876. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index dad549cf189cb..f2728790b8ae9 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -384,9 +384,6 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster, return USEC_PER_SEC * suspend_wake_time; } - cpumask_and(&online_cpus_in_cluster, - &cluster->num_childs_in_sync, cpu_online_mask); - for_each_cpu(cpu, &online_cpus_in_cluster) { td = &per_cpu(tick_cpu_device, cpu); if (td->evtdev->next_event.tv64 < next_event.tv64) { From 391b0fa45679b4af9e129f525909ec30d1c35fd2 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:21 +0100 Subject: [PATCH 097/296] Revert "drivers: lpm_levels: remove the BUG_ON in get_cluster_sleep_time" This reverts commit 1da77362747b61ee8175f1e66b728de68fbb1c39. Signed-off-by: anupritaisno1 --- 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 f2728790b8ae9..4eb1f69c6a5c5 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -384,6 +384,9 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster, return USEC_PER_SEC * suspend_wake_time; } + BUG_ON(!cpumask_and(&online_cpus_in_cluster, + &cluster->num_childs_in_sync, cpu_online_mask)); + for_each_cpu(cpu, &online_cpus_in_cluster) { td = &per_cpu(tick_cpu_device, cpu); if (td->evtdev->next_event.tv64 < next_event.tv64) { From a34c6f28b748af8a972752b49409144b1fa509e4 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:22 +0100 Subject: [PATCH 098/296] Revert "drivers: lpm-workarounds: Misc bug fixes" This reverts commit 948acbda44ef7b333693a0c42163010323b5dbf4. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 7 +++ drivers/cpuidle/lpm-workarounds.c | 96 ++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 27 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 4eb1f69c6a5c5..b136a2a6639b3 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -636,6 +636,13 @@ static void cluster_unprepare(struct lpm_cluster *cluster, level = &cluster->levels[cluster->last_level]; if (level->notify_rpm) { msm_rpm_exit_sleep(); + + /* If RPM bumps up CX to turbo, unvote CX turbo vote + * during exit of rpm assisted power collapse to + * reduce the power impact + */ + + lpm_wa_cx_unvote_send(); msm_mpm_exit_sleep(from_idle); } diff --git a/drivers/cpuidle/lpm-workarounds.c b/drivers/cpuidle/lpm-workarounds.c index 3311ff2ad5307..2155017cebad3 100644 --- a/drivers/cpuidle/lpm-workarounds.c +++ b/drivers/cpuidle/lpm-workarounds.c @@ -24,15 +24,15 @@ #include #include #include -#include #define L2_HS_STS_SET 0x200 +static struct regulator *lpm_cx_reg; static struct work_struct lpm_wa_work; static struct workqueue_struct *lpm_wa_wq; +static bool lpm_wa_cx_turbo_unvote; static bool skip_l2_spm; static bool enable_dynamic_clock_gating; -static bool is_l1_l2_gcc_secure; static bool store_clock_gating; int non_boot_cpu_index; void __iomem *l2_pwr_sts; @@ -41,26 +41,19 @@ struct device_clnt_data *hotplug_handle; union device_request curr_req; cpumask_t l1_l2_offline_mask; cpumask_t offline_mask; -struct resource *l1_l2_gcc_res; -uint32_t l2_status = -1; static int lpm_wa_callback(struct notifier_block *cpu_nb, unsigned long action, void *hcpu) { unsigned long cpu = (unsigned long) hcpu; - if ((action != CPU_POST_DEAD) && (action != CPU_ONLINE)) - return NOTIFY_OK; if (cpu >= non_boot_cpu_index) return NOTIFY_OK; switch (action & ~CPU_TASKS_FROZEN) { - case CPU_POST_DEAD: + case CPU_DEAD: cpumask_set_cpu(cpu, &offline_mask); break; - case CPU_ONLINE: - cpumask_clear_cpu(cpu, &offline_mask); - break; default: break; } @@ -80,6 +73,22 @@ static void process_lpm_workarounds(struct work_struct *w) { int ret = 0, status = 0; + /* While exiting from RPM assisted power collapse on some targets + * like MSM8939 the CX is bumped to turbo mode by RPM. To reduce + * the power impact, APSS low power driver need to remove the CX + * turbo vote. + */ + + if (lpm_wa_cx_turbo_unvote && lpm_cx_reg) { + regulator_set_voltage(lpm_cx_reg, + RPM_REGULATOR_CORNER_SUPER_TURBO, + RPM_REGULATOR_CORNER_SUPER_TURBO); + + regulator_set_voltage(lpm_cx_reg, + RPM_REGULATOR_CORNER_NONE, + RPM_REGULATOR_CORNER_SUPER_TURBO); + } + /* MSM8952 have L1/L2 dynamic clock gating disabled in HW for * performance cluster cores. Enable it via SW to reduce power * impact. @@ -96,23 +105,16 @@ static void process_lpm_workarounds(struct work_struct *w) if (status) { pr_err("%s: perf L2 is not in low power mode\n", __func__); - queue_work(lpm_wa_wq, &lpm_wa_work); return; } - l2_status = __raw_readl(l2_pwr_sts); - pr_err("Set L1_L2_GCC from cpu%d when perf L2 status=0x%x\n", - smp_processor_id(), l2_status); - - if (is_l1_l2_gcc_secure) { - scm_io_write((u32)(l1_l2_gcc_res->start), 0x0); - if (scm_io_read((u32)(l1_l2_gcc_res->start)) != 0) - pr_err("Failed to set L1_L2_GCC\n"); - } else { - __raw_writel(0x0, l1_l2_gcc); - if (__raw_readl(l1_l2_gcc) != 0x0) - pr_err("Failed to set L1_L2_GCC\n"); - } + pr_info("Set L1_L2_GCC from cpu%d when perf L2 status=0x%x\n", + smp_processor_id(), __raw_readl(l2_pwr_sts)); + __raw_writel(0x0, l1_l2_gcc); + + if (__raw_readl(l1_l2_gcc) != 0x0) + pr_err("Failed to set L1_L2_GCC\n"); + HOTPLUG_NO_MITIGATION(&curr_req.offline_mask); ret = devmgr_client_request_mitigation( hotplug_handle, @@ -127,6 +129,16 @@ static void process_lpm_workarounds(struct work_struct *w) } } +/* + * lpm_wa_cx_unvote_send(): Unvote for CX turbo mode + */ +void lpm_wa_cx_unvote_send(void) +{ + if (lpm_wa_cx_turbo_unvote) + queue_work(lpm_wa_wq, &lpm_wa_work); +} +EXPORT_SYMBOL(lpm_wa_cx_unvote_send); + bool lpm_wa_get_skip_l2_spm(void) { return skip_l2_spm; @@ -134,7 +146,28 @@ bool lpm_wa_get_skip_l2_spm(void) EXPORT_SYMBOL(lpm_wa_get_skip_l2_spm); +static int lpm_wa_cx_unvote_init(struct platform_device *pdev) +{ + int ret = 0; + + lpm_cx_reg = devm_regulator_get(&pdev->dev, "lpm-cx"); + if (IS_ERR(lpm_cx_reg)) { + ret = PTR_ERR(lpm_cx_reg); + if (ret != -EPROBE_DEFER) + pr_err("Unable to get the CX regulator\n"); + return ret; + } + + return ret; +} + +static int lpm_wa_cx_unvote_exit(void) +{ + if (lpm_wa_wq) + destroy_workqueue(lpm_wa_wq); + return 0; +} static ssize_t store_clock_gating_enabled(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { @@ -172,6 +205,16 @@ static int lpm_wa_probe(struct platform_device *pdev) unsigned long cpu_mask = 0; char *key; + lpm_wa_cx_turbo_unvote = of_property_read_bool(pdev->dev.of_node, + "qcom,lpm-wa-cx-turbo-unvote"); + if (lpm_wa_cx_turbo_unvote) { + ret = lpm_wa_cx_unvote_init(pdev); + if (ret) { + pr_err("%s: Failed to initialize lpm_wa_cx_unvote (%d)\n", + __func__, ret); + return ret; + } + } skip_l2_spm = of_property_read_bool(pdev->dev.of_node, "qcom,lpm-wa-skip-l2-spm"); @@ -185,8 +228,6 @@ static int lpm_wa_probe(struct platform_device *pdev) "qcom,lpm-wa-dynamic-clock-gating"); if (!enable_dynamic_clock_gating) return ret; - is_l1_l2_gcc_secure = of_property_read_bool(pdev->dev.of_node, - "qcom,l1_l2_gcc_secure"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "l2-pwr-sts"); @@ -211,7 +252,6 @@ static int lpm_wa_probe(struct platform_device *pdev) __func__); return -EINVAL; } - l1_l2_gcc_res = res; l1_l2_gcc = devm_ioremap(&pdev->dev, res->start, resource_size(res)); @@ -291,6 +331,8 @@ static int lpm_wa_probe(struct platform_device *pdev) static int lpm_wa_remove(struct platform_device *pdev) { int ret = 0; + if (lpm_wa_cx_turbo_unvote) + ret = lpm_wa_cx_unvote_exit(); return ret; } From c4d19dd200b1c684c12610d170e1c8d4808c1747 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:22 +0100 Subject: [PATCH 099/296] Revert "drivers: lpm-workarounds: Enable L1/L2 clock gating WA" This reverts commit 95efd05c568fb1db600f990de027bc3f7ce8698b. Signed-off-by: anupritaisno1 --- .../bindings/arm/msm/lpm-workarounds.txt | 23 +- drivers/cpuidle/lpm-workarounds.c | 248 +----------------- 2 files changed, 15 insertions(+), 256 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt b/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt index 0d420621acbf4..8f33c8208a00c 100644 --- a/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt +++ b/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt @@ -5,11 +5,6 @@ The required properties are: - compatible: "qcom,lpm-workarounds" The optional properties are: -- reg: The physical address and the size of the l1_l2_gcc and l2_pwr_sts - regitsters of performance cluster. - -- reg-names: "l2_pwr_sts" - string to identify l2_pwr_sts physical address. - "l1_l2_gcc" - string to identify l1_l2_gcc physical address. - qcom,lpm-wa-cx-turbo-unvote: Indicates the workaround to unvote CX turbo vote when system is coming out of rpm assisted power collaspe. @@ -26,28 +21,12 @@ The optional properties are: based on the L2 mode flag passed. Set lpm-wa-skip-l2-spm node if this is required. -- qcom,lpm-wa-dynamic-clock-gating: Due to a hardware bug on 8952, L1/L2 dynamic - clock gating needs to be enabled by software for performance cluster - cores and L2. Set lpm-wa-dynamic-clock-gating node if this workaround is - required. - -- qcom,cpu-offline-mask: Dynamic clock gating should be enabled when cluster is - in L2 PC. Each bit of cpu-offline-mask lists the cpu no. to hotplug by KTM - driver. - -- qcom,non-boot-cpu-index: will hold index of non boot cluster cpu. - Example: qcom,lpm-workarounds { compatible = "qcom,lpm-workarounds"; - reg = <0x0B011018 0x4>, - <0x0B011088 0x4>; - reg-names = "l2-pwr-sts", "l1-l2-gcc"; + lpm-cx-supply = <&pm8916_s2_corner>; qcom,lpm-wa-cx-turbo-unvote; qcom,lpm-wa-skip-l2-spm; - qcom,lpm-wa-dynamic-clock-gating; - qcom,cpu-offline-mask = "0xF"; - qcom,non-boot-cpu-index = <4>; } diff --git a/drivers/cpuidle/lpm-workarounds.c b/drivers/cpuidle/lpm-workarounds.c index 2155017cebad3..d50b2e1aecef9 100644 --- a/drivers/cpuidle/lpm-workarounds.c +++ b/drivers/cpuidle/lpm-workarounds.c @@ -13,73 +13,26 @@ #include #include -#include #include #include -#include #include #include #include #include -#include -#include -#include - -#define L2_HS_STS_SET 0x200 static struct regulator *lpm_cx_reg; -static struct work_struct lpm_wa_work; +static struct work_struct dummy_vote_work; static struct workqueue_struct *lpm_wa_wq; static bool lpm_wa_cx_turbo_unvote; static bool skip_l2_spm; -static bool enable_dynamic_clock_gating; -static bool store_clock_gating; -int non_boot_cpu_index; -void __iomem *l2_pwr_sts; -void __iomem *l1_l2_gcc; -struct device_clnt_data *hotplug_handle; -union device_request curr_req; -cpumask_t l1_l2_offline_mask; -cpumask_t offline_mask; - -static int lpm_wa_callback(struct notifier_block *cpu_nb, - unsigned long action, void *hcpu) -{ - unsigned long cpu = (unsigned long) hcpu; - - if (cpu >= non_boot_cpu_index) - return NOTIFY_OK; - - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_DEAD: - cpumask_set_cpu(cpu, &offline_mask); - break; - default: - break; - } - - if (cpumask_equal(&offline_mask, &l1_l2_offline_mask) - && store_clock_gating) - queue_work(lpm_wa_wq, &lpm_wa_work); - - return NOTIFY_OK; -} -static struct notifier_block __refdata lpm_wa_nblk = { - .notifier_call = lpm_wa_callback, -}; - -static void process_lpm_workarounds(struct work_struct *w) +/* While exiting from RPM assisted power collapse on some targets like MSM8939 + * the CX is bumped to turbo mode by RPM. To reduce the power impact, APSS + * low power driver need to remove the CX turbo vote. + */ +static void send_dummy_cx_vote(struct work_struct *w) { - int ret = 0, status = 0; - - /* While exiting from RPM assisted power collapse on some targets - * like MSM8939 the CX is bumped to turbo mode by RPM. To reduce - * the power impact, APSS low power driver need to remove the CX - * turbo vote. - */ - - if (lpm_wa_cx_turbo_unvote && lpm_cx_reg) { + if (lpm_cx_reg) { regulator_set_voltage(lpm_cx_reg, RPM_REGULATOR_CORNER_SUPER_TURBO, RPM_REGULATOR_CORNER_SUPER_TURBO); @@ -88,45 +41,6 @@ static void process_lpm_workarounds(struct work_struct *w) RPM_REGULATOR_CORNER_NONE, RPM_REGULATOR_CORNER_SUPER_TURBO); } - - /* MSM8952 have L1/L2 dynamic clock gating disabled in HW for - * performance cluster cores. Enable it via SW to reduce power - * impact. - */ - - if (enable_dynamic_clock_gating) { - - /* Skip enabling L1/L2 clock gating if perf l2 is not in low - * power mode. - */ - - status = (__raw_readl(l2_pwr_sts) & L2_HS_STS_SET) - == L2_HS_STS_SET; - if (status) { - pr_err("%s: perf L2 is not in low power mode\n", - __func__); - return; - } - - pr_info("Set L1_L2_GCC from cpu%d when perf L2 status=0x%x\n", - smp_processor_id(), __raw_readl(l2_pwr_sts)); - __raw_writel(0x0, l1_l2_gcc); - - if (__raw_readl(l1_l2_gcc) != 0x0) - pr_err("Failed to set L1_L2_GCC\n"); - - HOTPLUG_NO_MITIGATION(&curr_req.offline_mask); - ret = devmgr_client_request_mitigation( - hotplug_handle, - HOTPLUG_MITIGATION_REQ, - &curr_req); - if (ret) { - pr_err("hotplug request failed. err:%d\n", ret); - return; - } - enable_dynamic_clock_gating = false; - unregister_hotcpu_notifier(&lpm_wa_nblk); - } } /* @@ -135,7 +49,7 @@ static void process_lpm_workarounds(struct work_struct *w) void lpm_wa_cx_unvote_send(void) { if (lpm_wa_cx_turbo_unvote) - queue_work(lpm_wa_wq, &lpm_wa_work); + queue_work(lpm_wa_wq, &dummy_vote_work); } EXPORT_SYMBOL(lpm_wa_cx_unvote_send); @@ -158,6 +72,11 @@ static int lpm_wa_cx_unvote_init(struct platform_device *pdev) return ret; } + INIT_WORK(&dummy_vote_work, send_dummy_cx_vote); + + lpm_wa_wq = alloc_workqueue("lpm-wa", + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + return ret; } @@ -168,42 +87,10 @@ static int lpm_wa_cx_unvote_exit(void) return 0; } -static ssize_t store_clock_gating_enabled(struct kobject *kobj, - struct kobj_attribute *attr, const char *buf, size_t count) -{ - int ret = 0, val = 0; - - ret = kstrtoint(buf, 10, &val); - if (ret) { - pr_err("Invalid input%s %s. err:%d\n", __func__, buf, ret); - return count; - } - - cpumask_copy(&curr_req.offline_mask, &l1_l2_offline_mask); - ret = devmgr_client_request_mitigation( - hotplug_handle, - HOTPLUG_MITIGATION_REQ, - &curr_req); - if (ret) { - pr_err("hotplug request failed. err:%d\n", ret); - return count; - } - - store_clock_gating = true; - return count; -} - -static struct kobj_attribute clock_gating_enabled_attr = -__ATTR(dynamic_clock_gating, 0644, NULL, store_clock_gating_enabled); static int lpm_wa_probe(struct platform_device *pdev) { - int ret = 0, c = 0, cpu = 0; - struct kobject *module_kobj = NULL; - struct kobject *module_lpm_wa = NULL; - struct resource *res = NULL; - unsigned long cpu_mask = 0; - char *key; + int ret = 0; lpm_wa_cx_turbo_unvote = of_property_read_bool(pdev->dev.of_node, "qcom,lpm-wa-cx-turbo-unvote"); @@ -218,113 +105,6 @@ static int lpm_wa_probe(struct platform_device *pdev) skip_l2_spm = of_property_read_bool(pdev->dev.of_node, "qcom,lpm-wa-skip-l2-spm"); - - /* - * Enabling L1/L2 tag ram clock gating requires core and L2 to be - * in quiescent state. lpm-wa-dynamic-clock-gating flag specifies - * WA implementation in SW for perf core0 and L2. - */ - enable_dynamic_clock_gating = of_property_read_bool(pdev->dev.of_node, - "qcom,lpm-wa-dynamic-clock-gating"); - if (!enable_dynamic_clock_gating) - return ret; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "l2-pwr-sts"); - if (!res) { - pr_err("%s(): Missing perf l2-pwr-sts resource\n", - __func__); - return -EINVAL; - } - - l2_pwr_sts = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!l2_pwr_sts) { - pr_err("%s: cannot ioremap l2_pwr_status %s\n", __func__, - KBUILD_MODNAME); - return -ENOMEM; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "l1-l2-gcc"); - if (!res) { - pr_err("%s(): Missing L1/L2 GCC override resource\n", - __func__); - return -EINVAL; - } - - l1_l2_gcc = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!l1_l2_gcc) { - pr_err("%s: cannot ioremap L1/L2 GCC override %s\n", - __func__, KBUILD_MODNAME); - return -ENOMEM; - } - - key = "qcom,non-boot-cpu-index"; - ret = of_property_read_u32(pdev->dev.of_node, key, - &non_boot_cpu_index); - if (!ret) { - pr_err("%s: Missing qcom,non_boot_cpu_index property\n" - , __func__); - } - - key = "qcom,cpu-offline-mask"; - ret = of_property_read_u32(pdev->dev.of_node, key, - (u32 *) &cpu_mask); - if (!ret) { - for_each_set_bit(c, &cpu_mask, num_possible_cpus()) { - cpumask_set_cpu(c, &l1_l2_offline_mask); - } - } - - module_kobj = kset_find_obj(module_kset, "lpm_levels"); - if (!module_kobj) { - pr_err("%s: cannot find kobject for module lpm_levels\n", - __func__); - ret = -ENOENT; - } - - module_lpm_wa = kobject_create_and_add("lpm_workarounds", - module_kobj); - if (!module_lpm_wa) { - pr_err("%s: cannot create kobject for module %s\n", - __func__, KBUILD_MODNAME); - ret = -ENOENT; - } - - ret = sysfs_create_file(module_lpm_wa, - &clock_gating_enabled_attr.attr); - if (ret) { - pr_err("cannot create attr group. err:%d\n", ret); - return ret; - } - - hotplug_handle = devmgr_register_mitigation_client(&pdev->dev, - HOTPLUG_DEVICE, NULL); - if (IS_ERR_OR_NULL(hotplug_handle)) { - ret = PTR_ERR(hotplug_handle); - pr_err("Error registering for hotplug. ret:%d\n", ret); - return ret; - } - - INIT_WORK(&lpm_wa_work, process_lpm_workarounds); - - lpm_wa_wq = alloc_workqueue("lpm-wa", - WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); - if (IS_ERR_OR_NULL(lpm_wa_wq)) { - ret = PTR_ERR(lpm_wa_wq); - pr_err("%s: Failed to allocate workqueue\n", __func__); - return ret; - } - - for_each_present_cpu(cpu) { - if (cpu < non_boot_cpu_index) { - if (!cpumask_test_cpu(cpu, cpu_online_mask)) - cpumask_set_cpu(cpu, &offline_mask); - } - } - register_hotcpu_notifier(&lpm_wa_nblk); return ret; } From 7826c6c9d553ef0ad74631df9242dbe6988069ee Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:23 +0100 Subject: [PATCH 100/296] Revert "msm: spm: Add new API to set only SPM start address" This reverts commit 6c48b77b3ceeff4c0fd97a8e2f34ac2f2896449d. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 9 ++++----- drivers/soc/qcom/spm_devices.c | 18 +++++------------- include/soc/qcom/spm.h | 21 ++++----------------- 3 files changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index b136a2a6639b3..92f2ed172f97a 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -235,12 +235,11 @@ int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm) lpm = MSM_SPM_MODE_DISABLED; break; } - /* Do not program L2 SPM enable bit. This will be set by TZ */ + /* Do not program L2 SPM. This will be set by TZ */ if (lpm_wa_get_skip_l2_spm()) - rc = msm_spm_config_low_power_mode_addr(ops->spm, lpm, - notify_rpm); - else - rc = msm_spm_config_low_power_mode(ops->spm, lpm, notify_rpm); + return 0; + + rc = msm_spm_config_low_power_mode(ops->spm, lpm, notify_rpm); if (rc) pr_err("%s: Failed to set L2 low power mode %d, ERR %d", __func__, lpm, rc); diff --git a/drivers/soc/qcom/spm_devices.c b/drivers/soc/qcom/spm_devices.c index b773d0b1b32ce..849eb3901d8f4 100644 --- a/drivers/soc/qcom/spm_devices.c +++ b/drivers/soc/qcom/spm_devices.c @@ -186,7 +186,7 @@ static void msm_spm_config_q2s(struct msm_spm_device *dev, unsigned int mode) } static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev, - unsigned int mode, bool notify_rpm, bool set_spm_enable) + unsigned int mode, bool notify_rpm) { uint32_t i; uint32_t start_addr = 0; @@ -200,11 +200,9 @@ static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev, || (mode == MSM_SPM_MODE_GDHS)) pc_mode = true; - if (mode == MSM_SPM_MODE_DISABLED && set_spm_enable) { + if (mode == MSM_SPM_MODE_DISABLED) { ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false); - } else { - if (set_spm_enable) - ret = msm_spm_drv_set_spm_enable(&dev->reg_data, true); + } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) { for (i = 0; i < dev->num_modes; i++) { if (dev->modes[i].mode != mode) continue; @@ -354,7 +352,7 @@ bool msm_spm_is_mode_avail(unsigned int mode) int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm) { struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device); - return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, true); + return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm); } EXPORT_SYMBOL(msm_spm_set_low_power_mode); @@ -396,16 +394,10 @@ struct msm_spm_device *msm_spm_get_device_by_name(const char *name) return ERR_PTR(-ENODEV); } -int msm_spm_config_low_power_mode_addr(struct msm_spm_device *dev, - unsigned int mode, bool notify_rpm) -{ - return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, false); -} - int msm_spm_config_low_power_mode(struct msm_spm_device *dev, unsigned int mode, bool notify_rpm) { - return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, true); + return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm); } #ifdef CONFIG_MSM_L2_SPM diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h index c86e81d04375d..85486567cb205 100644 --- a/include/soc/qcom/spm.h +++ b/include/soc/qcom/spm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2014, 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,9 +25,6 @@ enum { struct msm_spm_device; struct device_node; -#define spm_raw_write(v, a) ({ __raw_writel(v, a); __raw_readl(a); }) -#define spm_write_relaxed(v, c) ({ writel_relaxed(v, c); readl_relaxed(c); }) - #if defined(CONFIG_MSM_SPM) int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm); @@ -39,8 +36,6 @@ int msm_spm_turn_on_cpu_rail(struct device_node *l2ccc_node, struct msm_spm_device *msm_spm_get_device_by_name(const char *name); int msm_spm_config_low_power_mode(struct msm_spm_device *dev, unsigned int mode, bool notify_rpm); -int msm_spm_config_low_power_mode_addr(struct msm_spm_device *dev, - unsigned int mode, bool notify_rpm); int msm_spm_device_init(void); bool msm_spm_is_mode_avail(unsigned int mode); void msm_spm_dump_regs(unsigned int cpu); @@ -100,25 +95,17 @@ static inline void msm_spm_dump_regs(unsigned int cpu) return; } -static inline int msm_spm_config_low_power_mode(struct msm_spm_device *dev, +int msm_spm_config_low_power_mode(struct msm_spm_device *dev, unsigned int mode, bool notify_rpm) { return -ENODEV; } -static inline int msm_spm_config_low_power_mode_addr( - struct msm_spm_device *dev, unsigned int mode, bool notify_rpm) -{ - return -ENODEV; -} - - -static inline struct msm_spm_device *msm_spm_get_device_by_name - (const char *name) +struct msm_spm_device *msm_spm_get_device_by_name(const char *name) { return NULL; } -static inline bool msm_spm_is_mode_avail(unsigned int mode) +bool msm_spm_is_mode_avail(unsigned int mode) { return false; } From b8dec907640d9d7198c4f593d5f9daccd32a8ff0 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:24 +0100 Subject: [PATCH 101/296] Revert "cpuidle: lpm-workarounds: Skip L2 spm programming in HLOS" This reverts commit fd244197d67e609b0b3182ee1c121517e987eda1. Signed-off-by: anupritaisno1 --- .../devicetree/bindings/arm/msm/lpm-workarounds.txt | 9 --------- drivers/cpuidle/lpm-levels.c | 5 +---- drivers/cpuidle/lpm-workarounds.c | 12 +----------- drivers/cpuidle/lpm-workarounds.h | 4 ++-- 4 files changed, 4 insertions(+), 26 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt b/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt index 8f33c8208a00c..bedfed5e5ccd8 100644 --- a/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt +++ b/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt @@ -13,14 +13,6 @@ The optional properties are: - lpm-cx-supply: will hold handle for CX regulator supply which is used to unvote. -- qcom,lpm-wa-skip-l2-spm: Due to a hardware bug on 8939 and 8909, secure - world needs to disable and enable L2 SPM to get the proper context - in secure watchdog bite cases. With this workaround there is a race - in programming L2 SPM between HLOS and secure world. This leads to - stability issues. To avoid this program L2 SPM only in secure world - based on the L2 mode flag passed. Set lpm-wa-skip-l2-spm node if this - is required. - Example: qcom,lpm-workarounds { @@ -28,5 +20,4 @@ qcom,lpm-workarounds { lpm-cx-supply = <&pm8916_s2_corner>; qcom,lpm-wa-cx-turbo-unvote; - qcom,lpm-wa-skip-l2-spm; } diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 92f2ed172f97a..0f7de2fcd8b38 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -235,11 +235,8 @@ int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm) lpm = MSM_SPM_MODE_DISABLED; break; } - /* Do not program L2 SPM. This will be set by TZ */ - if (lpm_wa_get_skip_l2_spm()) - return 0; - rc = msm_spm_config_low_power_mode(ops->spm, lpm, notify_rpm); + if (rc) pr_err("%s: Failed to set L2 low power mode %d, ERR %d", __func__, lpm, rc); diff --git a/drivers/cpuidle/lpm-workarounds.c b/drivers/cpuidle/lpm-workarounds.c index d50b2e1aecef9..104def7e71e48 100644 --- a/drivers/cpuidle/lpm-workarounds.c +++ b/drivers/cpuidle/lpm-workarounds.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 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,7 +24,6 @@ static struct regulator *lpm_cx_reg; static struct work_struct dummy_vote_work; static struct workqueue_struct *lpm_wa_wq; static bool lpm_wa_cx_turbo_unvote; -static bool skip_l2_spm; /* While exiting from RPM assisted power collapse on some targets like MSM8939 * the CX is bumped to turbo mode by RPM. To reduce the power impact, APSS @@ -53,13 +52,6 @@ void lpm_wa_cx_unvote_send(void) } EXPORT_SYMBOL(lpm_wa_cx_unvote_send); -bool lpm_wa_get_skip_l2_spm(void) -{ - return skip_l2_spm; -} -EXPORT_SYMBOL(lpm_wa_get_skip_l2_spm); - - static int lpm_wa_cx_unvote_init(struct platform_device *pdev) { int ret = 0; @@ -103,8 +95,6 @@ static int lpm_wa_probe(struct platform_device *pdev) } } - skip_l2_spm = of_property_read_bool(pdev->dev.of_node, - "qcom,lpm-wa-skip-l2-spm"); return ret; } diff --git a/drivers/cpuidle/lpm-workarounds.h b/drivers/cpuidle/lpm-workarounds.h index a296112f96168..c743691e1f585 100644 --- a/drivers/cpuidle/lpm-workarounds.h +++ b/drivers/cpuidle/lpm-workarounds.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 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 @@ -15,5 +15,5 @@ #define __LPM_WA_H void lpm_wa_cx_unvote_send(void); -bool lpm_wa_get_skip_l2_spm(void); + #endif /* __LPM_WA_H */ From 19a78cf41112cb0225907a8b6b0d12bbce08c0c0 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:25 +0100 Subject: [PATCH 102/296] Revert "msm: lpm-levels: Add ftrace events to track latency/sleep time" This reverts commit 4dbca17eac7c65a714b198612accca1781c2965d. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 3 +-- include/trace/events/trace_msm_low_power.h | 17 +++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 0f7de2fcd8b38..12eeee1e23946 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -354,8 +354,6 @@ static int cpu_power_select(struct cpuidle_device *dev, if (modified_time_us) msm_pm_set_timer(modified_time_us); - trace_cpu_idle_enter(best_level, sleep_us, latency_us, next_event_us); - return best_level; } @@ -741,6 +739,7 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, cpu_prepare(cluster, idx, true); cluster_prepare(cluster, cpumask, idx, true); + trace_cpu_idle_enter(idx); lpm_stats_cpu_enter(idx); success = msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, true); lpm_stats_cpu_exit(idx, success); diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h index ac9127875a2dc..2d56c89870621 100644 --- a/include/trace/events/trace_msm_low_power.h +++ b/include/trace/events/trace_msm_low_power.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2014-2015 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2014, 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,27 +20,20 @@ TRACE_EVENT(cpu_idle_enter, - TP_PROTO(int index, u32 sleep_us, u32 latency, u32 next_event_us), + TP_PROTO(int index), - TP_ARGS(index, sleep_us, latency, next_event_us), + TP_ARGS(index), TP_STRUCT__entry( __field(int, index) - __field(u32, sleep_us) - __field(u32, latency) - __field(u32, next_event_us) ), TP_fast_assign( __entry->index = index; - __entry->sleep_us = sleep_us; - __entry->latency = latency; - __entry->next_event_us = next_event_us; ), - TP_printk("idx:%d sleep_time:%u latency:%u next_event:%u", - __entry->index, __entry->sleep_us, __entry->latency, - __entry->next_event_us) + TP_printk("idx:%d", + __entry->index) ); TRACE_EVENT(cpu_idle_exit, From 46b6ffabba0b1eaad18f6f7282389dcc789ea052 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:26 +0100 Subject: [PATCH 103/296] Revert "lpm-levels: Add L3 flag to the smc call" This reverts commit 149d879b0a7755c4f90017a4ab789c646c48bb3d. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels-of.c | 2 +- drivers/cpuidle/lpm-levels.c | 31 ++++++------------------------- drivers/cpuidle/lpm-levels.h | 1 - include/soc/qcom/pm.h | 3 +-- 4 files changed, 8 insertions(+), 29 deletions(-) diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index a4995e2864b23..6a9671b0f5c53 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -280,7 +280,7 @@ static int parse_cluster_params(struct device_node *node, struct lpm_cluster *c) struct lpm_match match_tbl[] = { {"l2", set_l2_mode}, {"cci", set_system_mode}, - {"l3", set_l3_mode}, + {"l3", set_system_mode}, {"cbf", set_system_mode}, }; diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 12eeee1e23946..8811936719dbb 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -207,31 +207,28 @@ int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm) { int lpm = mode; int rc = 0; - struct low_power_ops *cpu_ops = per_cpu(cpu_cluster, - smp_processor_id())->lpm_dev; - - if (cpu_ops->tz_flag & MSM_SCM_L2_OFF || - cpu_ops->tz_flag & MSM_SCM_L2_GDHS) + if (ops->tz_flag == MSM_SCM_L2_OFF || + ops->tz_flag == MSM_SCM_L2_GDHS) coresight_cti_ctx_restore(); switch (mode) { case MSM_SPM_MODE_POWER_COLLAPSE: - cpu_ops->tz_flag = MSM_SCM_L2_OFF; + ops->tz_flag = MSM_SCM_L2_OFF; coresight_cti_ctx_save(); break; case MSM_SPM_MODE_GDHS: - cpu_ops->tz_flag = MSM_SCM_L2_GDHS; + ops->tz_flag = MSM_SCM_L2_GDHS; coresight_cti_ctx_save(); break; case MSM_SPM_MODE_CLOCK_GATING: case MSM_SPM_MODE_RETENTION: case MSM_SPM_MODE_DISABLED: - cpu_ops->tz_flag = MSM_SCM_L2_ON; + ops->tz_flag = MSM_SCM_L2_ON; break; default: - cpu_ops->tz_flag = MSM_SCM_L2_ON; + ops->tz_flag = MSM_SCM_L2_ON; lpm = MSM_SPM_MODE_DISABLED; break; } @@ -244,22 +241,6 @@ 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) -{ - struct low_power_ops *cpu_ops = per_cpu(cpu_cluster, - smp_processor_id())->lpm_dev; - - switch (mode) { - case MSM_SPM_MODE_POWER_COLLAPSE: - cpu_ops->tz_flag |= MSM_SCM_L3_PC_OFF; - break; - default: - break; - } - return msm_spm_config_low_power_mode(ops->spm, mode, notify_rpm); -} - - int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm) { if (mode == MSM_SPM_MODE_CLOCK_GATING) diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index 17dbdd94737fe..c3ef800dde7c8 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -94,7 +94,6 @@ struct lpm_cluster { int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm); void lpm_suspend_wake_time(uint64_t wakeup_time); 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); struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev); void free_cluster_node(struct lpm_cluster *cluster); diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h index 4801275c28ba8..076ab9c39b1f7 100644 --- a/include/soc/qcom/pm.h +++ b/include/soc/qcom/pm.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. * Author: San Mehat * * This software is licensed under the terms of the GNU General Public @@ -45,7 +45,6 @@ enum msm_pm_l2_scm_flag { MSM_SCM_L2_OFF = 1, MSM_SCM_L2_RET = 2, MSM_SCM_L2_GDHS = 3, - MSM_SCM_L3_PC_OFF = 4, }; #define MSM_PM_MODE(cpu, mode_nr) ((cpu) * MSM_PM_SLEEP_MODE_NR + (mode_nr)) From 70989ae35fa10471ce072efb5eacfbfa1bb0c54b Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:26 +0100 Subject: [PATCH 104/296] Revert "lpm-levels: Support l3 and cbf modes" This reverts commit 83cf0caf208773219634bc8f09c27d2e96b7d966. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels-of.c | 4 +--- drivers/cpuidle/lpm-levels.c | 2 +- drivers/cpuidle/lpm-levels.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 6a9671b0f5c53..1f6fa199ba6ae 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -279,9 +279,7 @@ static int parse_cluster_params(struct device_node *node, struct lpm_cluster *c) }; struct lpm_match match_tbl[] = { {"l2", set_l2_mode}, - {"cci", set_system_mode}, - {"l3", set_system_mode}, - {"cbf", set_system_mode}, + {"cci", set_cci_mode}, }; key = "label"; diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 8811936719dbb..5a2c3fe235507 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -241,7 +241,7 @@ int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm) return rc; } -int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm) +int set_cci_mode(struct low_power_ops *ops, int mode, bool notify_rpm) { if (mode == MSM_SPM_MODE_CLOCK_GATING) mode = MSM_SPM_MODE_DISABLED; diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index c3ef800dde7c8..5cdde6fa04a33 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -92,8 +92,8 @@ struct lpm_cluster { }; int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm); +int set_cci_mode(struct low_power_ops *ops, int mode, bool notify_rpm); void lpm_suspend_wake_time(uint64_t wakeup_time); -int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm); struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev); void free_cluster_node(struct lpm_cluster *cluster); From 658ca8f16caa93a3d90e995a7ec7e9c3f8c9af42 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:27 +0100 Subject: [PATCH 105/296] Revert "drivers: lpm-levels: Provide API to set wake up time from suspend" This reverts commit 54dc6f9449a4d028147d5f5690f974aa5f53b118. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels.c | 23 +++-------------------- drivers/cpuidle/lpm-levels.h | 1 - 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 5a2c3fe235507..f87f74e80761e 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -102,7 +102,6 @@ module_param_named( static int msm_pm_sleep_time_override; module_param_named(sleep_time_override, msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP); -static uint64_t suspend_wake_time; static bool print_parsed_dt; module_param_named( @@ -118,21 +117,6 @@ s32 msm_cpuidle_get_deep_idle_latency(void) return 10; } -void lpm_suspend_wake_time(uint64_t wakeup_time) -{ - if (wakeup_time <= 0) { - suspend_wake_time = msm_pm_sleep_time_override; - return; - } - - if (msm_pm_sleep_time_override && - (msm_pm_sleep_time_override < wakeup_time)) - suspend_wake_time = msm_pm_sleep_time_override; - else - suspend_wake_time = wakeup_time; -} -EXPORT_SYMBOL(lpm_suspend_wake_time); - static void update_debug_pc_event(enum debug_event event, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) { @@ -348,15 +332,14 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster, struct cpumask online_cpus_in_cluster; next_event.tv64 = KTIME_MAX; - if (!suspend_wake_time) - suspend_wake_time = msm_pm_sleep_time_override; + if (!from_idle) { if (mask) cpumask_copy(mask, cpumask_of(raw_smp_processor_id())); - if (!suspend_wake_time) + if (!msm_pm_sleep_time_override) return ~0ULL; else - return USEC_PER_SEC * suspend_wake_time; + return USEC_PER_SEC * msm_pm_sleep_time_override; } BUG_ON(!cpumask_and(&online_cpus_in_cluster, diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index 5cdde6fa04a33..09bb4c12fddbc 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -93,7 +93,6 @@ struct lpm_cluster { int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm); int set_cci_mode(struct low_power_ops *ops, int mode, bool notify_rpm); -void lpm_suspend_wake_time(uint64_t wakeup_time); struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev); void free_cluster_node(struct lpm_cluster *cluster); From 3cc7da3e3546ad9bc2f0ec17625a7a08b2027d37 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:28 +0100 Subject: [PATCH 106/296] Revert "msm: lpm-levels-of: Fix lockdep warnings" This reverts commit a697915c80abb41b6e66c8f42a4ebfbaf4cd9275. Signed-off-by: anupritaisno1 --- drivers/cpuidle/lpm-levels-of.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 1f6fa199ba6ae..4f0305ec9be56 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -112,13 +112,11 @@ static int create_lvl_avail_nodes(const char *name, goto failed; } - sysfs_attr_init(&avail->idle_enabled_attr.attr); avail->idle_enabled_attr.attr.name = lpm_types[IDLE].str; avail->idle_enabled_attr.attr.mode = 0644; avail->idle_enabled_attr.show = lpm_enable_show; avail->idle_enabled_attr.store = lpm_enable_store; - sysfs_attr_init(&avail->suspend_enabled_attr.attr); avail->suspend_enabled_attr.attr.name = lpm_types[SUSPEND].str; avail->suspend_enabled_attr.attr.mode = 0644; avail->suspend_enabled_attr.show = lpm_enable_show; From f6441e77902106add75467a92ee6908275cab7cd Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 22:36:29 +0100 Subject: [PATCH 107/296] Revert "cpuidle: lpm-levels: Disable dynamic routing of interrupts" This reverts commit edd5fc7f598c0013539fc818db0b969b586a2452. Signed-off-by: anupritaisno1 --- Documentation/devicetree/bindings/arm/msm/lpm-levels.txt | 2 -- drivers/cpuidle/lpm-levels-of.c | 2 -- drivers/cpuidle/lpm-levels.c | 7 +++---- drivers/cpuidle/lpm-levels.h | 1 - 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt index 3ca6a36116276..976c2f6608818 100644 --- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt +++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt @@ -71,8 +71,6 @@ Required properties: sleep. - qcom,last-level - When set, the cluster level is applied only when there is 1 online core. - - qcom,disable-dynamic-int-routing: When set disables the dynamic - routing of rpm-smd and mpm interrupts to next wake up core. [Node bindings for qcom,pm-cpu] qcom,pm-cpu contains the low power modes that a cpu could enter. Currently it diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 4f0305ec9be56..5926916fce5fe 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -441,8 +441,6 @@ static int parse_cluster_level(struct device_node *node, } level->notify_rpm = of_property_read_bool(node, "qcom,notify-rpm"); - level->disable_dynamic_routing = of_property_read_bool(node, - "qcom,disable-dynamic-int-routing"); level->last_core_only = of_property_read_bool(node, "qcom,last-core-only"); diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index f87f74e80761e..e93b587102aba 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -477,20 +477,19 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, cpu_cluster_pm_enter(cluster->aff_level); } if (level->notify_rpm) { - struct cpumask nextcpu, *cpumask; + struct cpumask nextcpu; uint32_t us; us = get_cluster_sleep_time(cluster, &nextcpu, from_idle); - cpumask = level->disable_dynamic_routing ? NULL : &nextcpu; - ret = msm_rpm_enter_sleep(0, cpumask); + ret = msm_rpm_enter_sleep(0, &nextcpu); if (ret) { pr_info("Failed msm_rpm_enter_sleep() rc = %d\n", ret); goto failed_set_mode; } do_div(us, USEC_PER_SEC/SCLK_HZ); - msm_mpm_enter_sleep((uint32_t)us, from_idle, cpumask); + msm_mpm_enter_sleep((uint32_t)us, from_idle, &nextcpu); } cluster->last_level = idx; spin_unlock(&cluster->sync_lock); diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index 09bb4c12fddbc..04e46c7cebab8 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -55,7 +55,6 @@ struct lpm_cluster_level { struct cpumask num_cpu_votes; struct power_params pwr; bool notify_rpm; - bool disable_dynamic_routing; bool sync_level; bool last_core_only; struct lpm_level_avail available; From dbfa9cdf45cc78cb75b1ba668f4d6af59dc844d5 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 13 Jun 2018 23:09:07 +0100 Subject: [PATCH 108/296] Revert "cpuidle: declare cpuidle_dev in cpuidle.h" This reverts commit 31ad61233ad633e305d5442183c4673a3a3b3e4a. Signed-off-by: anupritaisno1 --- include/linux/cpuidle.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 6282f8d88ebc3..8f0406230a0a4 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -83,7 +83,6 @@ struct cpuidle_device { }; DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); -DECLARE_PER_CPU(struct cpuidle_device, cpuidle_dev); /** * cpuidle_get_last_residency - retrieves the last state's residency time From 06fae098e7d914093308a5ef44e44dbc9aea8476 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:33:15 +0100 Subject: [PATCH 109/296] Revert "msm: mdss: kcal: Only apply updates on index 0" This reverts commit 17a84a108e5d3c541fe070b1739f573b0721b04c. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index fa38f7627856c..cb6734839aa15 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -50,8 +50,7 @@ static int mdss_mdp_kcal_display_commit(void) for (i = 0; i < mdata->nctl; i++) { ctl = mdata->ctl_off + i; /* pp setup requires mfd */ - if (mdss_mdp_ctl_is_power_on(ctl) && ctl->mfd && - ctl->mfd->index == 0) { + if ((mdss_mdp_ctl_is_power_on(ctl)) && (ctl->mfd)) { ret = mdss_mdp_pp_setup(ctl); if (ret) pr_err("%s: setup failed: %d\n", __func__, ret); From 213a37fe4b5e83553e38ef085b2264ff2e758448 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:34:08 +0100 Subject: [PATCH 110/296] Revert "msm: mdss: kcal: Use PCC for negative display mode" This reverts commit c54a80f6d2e155e28543f66637ba1fb75bc69433. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 128 ++++++++++++++++++-- 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index cb6734839aa15..582c3391f31fc 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -40,6 +40,98 @@ struct kcal_lut_data { int cont; }; +static uint32_t igc_inverted[IGC_LUT_ENTRIES] = { + 267390960, 266342368, 265293776, 264245184, + 263196592, 262148000, 261099408, 260050816, + 259002224, 257953632, 256905040, 255856448, + 254807856, 253759264, 252710672, 251662080, + 250613488, 249564896, 248516304, 247467712, + 246419120, 245370528, 244321936, 243273344, + 242224752, 241176160, 240127568, 239078976, + 238030384, 236981792, 235933200, 234884608, + 233836016, 232787424, 231738832, 230690240, + 229641648, 228593056, 227544464, 226495872, + 225447280, 224398688, 223350096, 222301504, + 221252912, 220204320, 219155728, 218107136, + 217058544, 216009952, 214961360, 213912768, + 212864176, 211815584, 210766992, 209718400, + 208669808, 207621216, 206572624, 205524032, + 204475440, 203426848, 202378256, 201329664, + 200281072, 199232480, 198183888, 197135296, + 196086704, 195038112, 193989520, 192940928, + 191892336, 190843744, 189795152, 188746560, + 187697968, 186649376, 185600784, 184552192, + 183503600, 182455008, 181406416, 180357824, + 179309232, 178260640, 177212048, 176163456, + 175114864, 174066272, 173017680, 171969088, + 170920496, 169871904, 168823312, 167774720, + 166726128, 165677536, 164628944, 163580352, + 162531760, 161483168, 160434576, 159385984, + 158337392, 157288800, 156240208, 155191616, + 154143024, 153094432, 152045840, 150997248, + 149948656, 148900064, 147851472, 146802880, + 145754288, 144705696, 143657104, 142608512, + 141559920, 140511328, 139462736, 138414144, + 137365552, 136316960, 135268368, 134219776, + 133171184, 132122592, 131074000, 130025408, + 128976816, 127928224, 126879632, 125831040, + 124782448, 123733856, 122685264, 121636672, + 120588080, 119539488, 118490896, 117442304, + 116393712, 115345120, 114296528, 113247936, + 112199344, 111150752, 110102160, 109053568, + 108004976, 106956384, 105907792, 104859200, + 103810608, 102762016, 101713424, 100664832, + 99616240, 98567648, 97519056, 96470464, + 95421872, 94373280, 93324688, 92276096, + 91227504, 90178912, 89130320, 88081728, + 87033136, 85984544, 84935952, 83887360, + 82838768, 81790176, 80741584, 79692992, + 78644400, 77595808, 76547216, 75498624, + 74450032, 73401440, 72352848, 71304256, + 70255664, 69207072, 68158480, 67109888, + 66061296, 65012704, 63964112, 62915520, + 61866928, 60818336, 59769744, 58721152, + 57672560, 56623968, 55575376, 54526784, + 53478192, 52429600, 51381008, 50332416, + 49283824, 48235232, 47186640, 46138048, + 45089456, 44040864, 42992272, 41943680, + 40895088, 39846496, 38797904, 37749312, + 36700720, 35652128, 34603536, 33554944, + 32506352, 31457760, 30409168, 29360576, + 28311984, 27263392, 26214800, 25166208, + 24117616, 23069024, 22020432, 20971840, + 19923248, 18874656, 17826064, 16777472, + 15728880, 14680288, 13631696, 12583104, + 11534512, 10485920, 9437328, 8388736, + 7340144, 6291552, 5242960, 4194368, + 3145776, 2097184, 1048592, 0 +}; + +static uint32_t igc_rgb[IGC_LUT_ENTRIES] = { + 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, + 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, + 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, + 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, + 3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, + 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, + 2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, + 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, + 2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, + 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, + 2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, + 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, + 1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, + 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, + 1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, + 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, + 1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, + 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, + 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, + 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, + 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, + 48, 32, 16, 0 +}; + static int mdss_mdp_kcal_display_commit(void) { int i; @@ -82,14 +174,6 @@ static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) pcc_config.g.g = lut_data->green * PCC_ADJ; pcc_config.b.b = lut_data->blue * PCC_ADJ; - if (lut_data->invert) { - pcc_config.r.c = pcc_config.g.c = - pcc_config.b.c = 0x7ff8; - pcc_config.r.r |= (0xffff << 16); - pcc_config.g.g |= (0xffff << 16); - pcc_config.b.b |= (0xffff << 16); - } - mdss_mdp_pcc_config(&pcc_config, ©back); } @@ -111,9 +195,9 @@ static void mdss_mdp_kcal_read_pcc(struct kcal_lut_data *lut_data) if (!pcc_config.r.r && !pcc_config.g.g && !pcc_config.b.b) return; - lut_data->red = (pcc_config.r.r & 0xffff) / PCC_ADJ; - lut_data->green = (pcc_config.g.g & 0xffff) / PCC_ADJ; - lut_data->blue = (pcc_config.b.b & 0xffff) / PCC_ADJ; + lut_data->red = pcc_config.r.r / PCC_ADJ; + lut_data->green = pcc_config.g.g / PCC_ADJ; + lut_data->blue = pcc_config.b.b / PCC_ADJ; } static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) @@ -160,6 +244,24 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) } } +static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0, copy_from_kernel = 1; + struct mdp_igc_lut_data igc_config; + + memset(&igc_config, 0, sizeof(struct mdp_igc_lut_data)); + + igc_config.block = MDP_LOGICAL_BLOCK_DISP_0; + igc_config.ops = lut_data->invert && lut_data->enable ? + MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + igc_config.len = IGC_LUT_ENTRIES; + igc_config.c0_c1_data = igc_inverted; + igc_config.c2_data = igc_rgb; + + mdss_mdp_igc_lut_config(&igc_config, ©back, copy_from_kernel); +} + static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -233,6 +335,7 @@ static ssize_t kcal_enable_store(struct device *dev, mdss_mdp_kcal_update_pcc(lut_data); mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); mdss_mdp_kcal_display_commit(); return count; @@ -259,7 +362,7 @@ static ssize_t kcal_invert_store(struct device *dev, lut_data->invert = kcal_invert; - mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_kcal_update_igc(lut_data); mdss_mdp_kcal_display_commit(); return count; @@ -416,6 +519,7 @@ static int kcal_ctrl_probe(struct platform_device *pdev) mdss_mdp_kcal_update_pcc(lut_data); mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); mdss_mdp_kcal_display_commit(); ret = device_create_file(&pdev->dev, &dev_attr_kcal); From e85502b957f8eb908d39ee9f7ecc8e04854fa9fd Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:38:41 +0100 Subject: [PATCH 111/296] Revert "msm: mdss: KCAL: Send a pp display commit when changes are made" This reverts commit 21d8d946ac8ea7c3ee3469f2f772bf926f17f195. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 181 ++++++++++++++++---- 1 file changed, 143 insertions(+), 38 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 582c3391f31fc..78f79d45f06da 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -21,6 +21,15 @@ #include #include +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) && defined(CONFIG_FB) +#include +#include +#include +#elif defined(CONFIG_FB) +#include +#include +#endif + #include "mdss_mdp.h" #define DEF_PCC 0x100 @@ -28,6 +37,13 @@ #define PCC_ADJ 0x80 struct kcal_lut_data { +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) && defined(CONFIG_FB) + struct mmi_notifier panel_nb; +#elif defined(CONFIG_FB) + struct device dev; + struct notifier_block panel_nb; +#endif + bool queue_changes; int red; int green; int blue; @@ -132,24 +148,19 @@ static uint32_t igc_rgb[IGC_LUT_ENTRIES] = { 48, 32, 16, 0 }; -static int mdss_mdp_kcal_display_commit(void) +static bool mdss_mdp_kcal_is_panel_on(void) { int i; - int ret = 0; struct mdss_mdp_ctl *ctl; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); for (i = 0; i < mdata->nctl; i++) { ctl = mdata->ctl_off + i; - /* pp setup requires mfd */ - if ((mdss_mdp_ctl_is_power_on(ctl)) && (ctl->mfd)) { - ret = mdss_mdp_pp_setup(ctl); - if (ret) - pr_err("%s: setup failed: %d\n", __func__, ret); - } + if (mdss_mdp_ctl_is_power_on(ctl)) + return true; } - return ret; + return false; } static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) @@ -159,13 +170,6 @@ static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); - lut_data->red = lut_data->red < lut_data->minimum ? - lut_data->minimum : lut_data->red; - lut_data->green = lut_data->green < lut_data->minimum ? - lut_data->minimum : lut_data->green; - lut_data->blue = lut_data->blue < lut_data->minimum ? - lut_data->minimum : lut_data->blue; - pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; pcc_config.ops = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : @@ -262,6 +266,16 @@ static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) mdss_mdp_igc_lut_config(&igc_config, ©back, copy_from_kernel); } +static void mdss_mdp_kcal_check_pcc(struct kcal_lut_data *lut_data) +{ + lut_data->red = lut_data->red < lut_data->minimum ? + lut_data->minimum : lut_data->red; + lut_data->green = lut_data->green < lut_data->minimum ? + lut_data->minimum : lut_data->green; + lut_data->blue = lut_data->blue < lut_data->minimum ? + lut_data->minimum : lut_data->blue; +} + static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -277,8 +291,12 @@ static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, lut_data->green = kcal_g; lut_data->blue = kcal_b; - mdss_mdp_kcal_update_pcc(lut_data); - mdss_mdp_kcal_display_commit(); + mdss_mdp_kcal_check_pcc(lut_data); + + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pcc(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -288,7 +306,8 @@ static ssize_t kcal_show(struct device *dev, struct device_attribute *attr, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - mdss_mdp_kcal_read_pcc(lut_data); + if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) + mdss_mdp_kcal_read_pcc(lut_data); return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", lut_data->red, lut_data->green, lut_data->blue); @@ -306,8 +325,12 @@ static ssize_t kcal_min_store(struct device *dev, lut_data->minimum = kcal_min; - mdss_mdp_kcal_update_pcc(lut_data); - mdss_mdp_kcal_display_commit(); + mdss_mdp_kcal_check_pcc(lut_data); + + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pcc(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -333,10 +356,12 @@ static ssize_t kcal_enable_store(struct device *dev, lut_data->enable = kcal_enable; - mdss_mdp_kcal_update_pcc(lut_data); - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_update_igc(lut_data); - mdss_mdp_kcal_display_commit(); + if (mdss_mdp_kcal_is_panel_on()) { + mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); + } else + lut_data->queue_changes = true; return count; } @@ -362,8 +387,10 @@ static ssize_t kcal_invert_store(struct device *dev, lut_data->invert = kcal_invert; - mdss_mdp_kcal_update_igc(lut_data); - mdss_mdp_kcal_display_commit(); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_igc(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -373,6 +400,8 @@ static ssize_t kcal_invert_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + /* IGC lut does not support reading regs in kernel space yet */ + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->invert); } @@ -388,8 +417,10 @@ static ssize_t kcal_sat_store(struct device *dev, lut_data->sat = kcal_sat; - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_display_commit(); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -414,8 +445,10 @@ static ssize_t kcal_hue_store(struct device *dev, lut_data->hue = kcal_hue; - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_display_commit(); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -440,8 +473,10 @@ static ssize_t kcal_val_store(struct device *dev, lut_data->val = kcal_val; - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_display_commit(); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -466,8 +501,10 @@ static ssize_t kcal_cont_store(struct device *dev, lut_data->cont = kcal_cont; - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_display_commit(); + if (mdss_mdp_kcal_is_panel_on()) + mdss_mdp_kcal_update_pa(lut_data); + else + lut_data->queue_changes = true; return count; } @@ -492,6 +529,39 @@ static DEVICE_ATTR(kcal_val, S_IWUSR | S_IRUGO, kcal_val_show, kcal_val_store); static DEVICE_ATTR(kcal_cont, S_IWUSR | S_IRUGO, kcal_cont_show, kcal_cont_store); +static int mdss_mdp_kcal_update_queue(struct device *dev) +{ + struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + + if (lut_data->queue_changes) { + mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); + lut_data->queue_changes = false; + } + + return 0; +} + +#if defined(CONFIG_FB) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + struct kcal_lut_data *lut_data = + container_of(nb, struct kcal_lut_data, panel_nb); + + if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + mdss_mdp_kcal_update_queue(&lut_data->dev); + } + + return 0; +} +#endif + static int kcal_ctrl_probe(struct platform_device *pdev) { int ret; @@ -517,10 +587,29 @@ static int kcal_ctrl_probe(struct platform_device *pdev) lut_data->val = DEF_PA; lut_data->cont = DEF_PA; + lut_data->queue_changes = false; + mdss_mdp_kcal_update_pcc(lut_data); mdss_mdp_kcal_update_pa(lut_data); mdss_mdp_kcal_update_igc(lut_data); - mdss_mdp_kcal_display_commit(); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + lut_data->panel_nb.display_on = mdss_mdp_kcal_update_queue; + lut_data->panel_nb.dev = &pdev->dev; + ret = mmi_panel_register_notifier(&lut_data->panel_nb); + if (ret) { + pr_err("%s: unable to register MMI notifier\n", __func__); + return ret; + } +#elif defined(CONFIG_FB) + lut_data->dev = pdev->dev; + lut_data->panel_nb.notifier_call = fb_notifier_callback; + ret = fb_register_client(&lut_data->panel_nb); + if (ret) { + pr_err("%s: unable to register fb notifier\n", __func__); + return ret; + } +#endif ret = device_create_file(&pdev->dev, &dev_attr_kcal); ret |= device_create_file(&pdev->dev, &dev_attr_kcal_min); @@ -532,14 +621,24 @@ static int kcal_ctrl_probe(struct platform_device *pdev) ret |= device_create_file(&pdev->dev, &dev_attr_kcal_cont); if (ret) { pr_err("%s: unable to create sysfs entries\n", __func__); - return ret; + goto out_notifier; } return 0; + +out_notifier: +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&lut_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&lut_data->panel_nb); +#endif + return ret; } static int kcal_ctrl_remove(struct platform_device *pdev) { + struct kcal_lut_data *lut_data = platform_get_drvdata(pdev); + device_remove_file(&pdev->dev, &dev_attr_kcal); device_remove_file(&pdev->dev, &dev_attr_kcal_min); device_remove_file(&pdev->dev, &dev_attr_kcal_enable); @@ -549,6 +648,12 @@ static int kcal_ctrl_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_kcal_val); device_remove_file(&pdev->dev, &dev_attr_kcal_cont); +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&lut_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&lut_data->panel_nb); +#endif + return 0; } @@ -583,5 +688,5 @@ static void __exit kcal_ctrl_exit(void) platform_driver_unregister(&kcal_ctrl_driver); } -module_init(kcal_ctrl_init); +late_initcall(kcal_ctrl_init); module_exit(kcal_ctrl_exit); From 91aff430b8908ec839b3ee500e4347a3065a4a4a Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:39:30 +0100 Subject: [PATCH 112/296] Revert "msm: mdss: KCAL: Do not read from picture adjustment registers" This reverts commit 54ad8c4823b94a0189d781619c23bfdb5d48ac49. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 78f79d45f06da..8f4de0206e596 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -248,6 +248,44 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) } } +static void mdss_mdp_kcal_read_pa(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0; + struct mdp_pa_cfg_data pa_config; + struct mdp_pa_v2_cfg_data pa_v2_config; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + if (mdata->mdp_rev < MDSS_MDP_HW_REV_103) { + memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); + + pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_config.pa_data.flags = MDP_PP_OPS_READ; + + mdss_mdp_pa_config(&pa_config, ©back); + + lut_data->hue = pa_config.pa_data.hue_adj; + lut_data->sat = pa_config.pa_data.sat_adj; + lut_data->val = pa_config.pa_data.val_adj; + lut_data->cont = pa_config.pa_data.cont_adj; + } else { + memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); + + pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_v2_config.pa_v2_data.flags = MDP_PP_OPS_READ; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_ENABLE; + + mdss_mdp_pa_v2_config(&pa_v2_config, ©back); + + lut_data->hue = pa_v2_config.pa_v2_data.global_hue_adj; + lut_data->sat = pa_v2_config.pa_v2_data.global_sat_adj; + lut_data->val = pa_v2_config.pa_v2_data.global_val_adj; + lut_data->cont = pa_v2_config.pa_v2_data.global_cont_adj; + } +} + static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) { u32 copyback = 0, copy_from_kernel = 1; @@ -430,6 +468,9 @@ static ssize_t kcal_sat_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) + mdss_mdp_kcal_read_pa(lut_data); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->sat); } @@ -458,6 +499,9 @@ static ssize_t kcal_hue_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) + mdss_mdp_kcal_read_pa(lut_data); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->hue); } @@ -486,6 +530,9 @@ static ssize_t kcal_val_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) + mdss_mdp_kcal_read_pa(lut_data); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->val); } @@ -514,6 +561,9 @@ static ssize_t kcal_cont_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); + if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) + mdss_mdp_kcal_read_pa(lut_data); + return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->cont); } From 33c24944dff7dd05cd78f5f7434b925fbd579ad5 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:39:58 +0100 Subject: [PATCH 113/296] Revert "msm: mdss: KCAL: Read values from MDP registers wherever possible" This reverts commit a4653b3453d3c4f1657a82fd12725df9811f807d. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 113 +++----------------- 1 file changed, 14 insertions(+), 99 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 8f4de0206e596..dfb88580ab184 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -56,7 +56,7 @@ struct kcal_lut_data { int cont; }; -static uint32_t igc_inverted[IGC_LUT_ENTRIES] = { +static uint32_t igc_Table_Inverted[IGC_LUT_ENTRIES] = { 267390960, 266342368, 265293776, 264245184, 263196592, 262148000, 261099408, 260050816, 259002224, 257953632, 256905040, 255856448, @@ -123,7 +123,7 @@ static uint32_t igc_inverted[IGC_LUT_ENTRIES] = { 3145776, 2097184, 1048592, 0 }; -static uint32_t igc_rgb[IGC_LUT_ENTRIES] = { +static uint32_t igc_Table_RGB[IGC_LUT_ENTRIES] = { 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, @@ -168,6 +168,13 @@ static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) u32 copyback = 0; struct mdp_pcc_cfg_data pcc_config; + lut_data->red = lut_data->red < lut_data->minimum ? + lut_data->minimum : lut_data->red; + lut_data->green = lut_data->green < lut_data->minimum ? + lut_data->minimum : lut_data->green; + lut_data->blue = lut_data->blue < lut_data->minimum ? + lut_data->minimum : lut_data->blue; + memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; @@ -181,29 +188,6 @@ static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) mdss_mdp_pcc_config(&pcc_config, ©back); } -static void mdss_mdp_kcal_read_pcc(struct kcal_lut_data *lut_data) -{ - u32 copyback = 0; - struct mdp_pcc_cfg_data pcc_config; - - memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); - - pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pcc_config.ops = MDP_PP_OPS_READ; - - mdss_mdp_pcc_config(&pcc_config, ©back); - - /* LiveDisplay disables pcc when using default values and regs - * are zeroed on pp resume, so throw these values out. - */ - if (!pcc_config.r.r && !pcc_config.g.g && !pcc_config.b.b) - return; - - lut_data->red = pcc_config.r.r / PCC_ADJ; - lut_data->green = pcc_config.g.g / PCC_ADJ; - lut_data->blue = pcc_config.b.b / PCC_ADJ; -} - static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) { u32 copyback = 0; @@ -248,44 +232,6 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) } } -static void mdss_mdp_kcal_read_pa(struct kcal_lut_data *lut_data) -{ - u32 copyback = 0; - struct mdp_pa_cfg_data pa_config; - struct mdp_pa_v2_cfg_data pa_v2_config; - struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - - if (mdata->mdp_rev < MDSS_MDP_HW_REV_103) { - memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); - - pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_config.pa_data.flags = MDP_PP_OPS_READ; - - mdss_mdp_pa_config(&pa_config, ©back); - - lut_data->hue = pa_config.pa_data.hue_adj; - lut_data->sat = pa_config.pa_data.sat_adj; - lut_data->val = pa_config.pa_data.val_adj; - lut_data->cont = pa_config.pa_data.cont_adj; - } else { - memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); - - pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_v2_config.pa_v2_data.flags = MDP_PP_OPS_READ; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_ENABLE; - - mdss_mdp_pa_v2_config(&pa_v2_config, ©back); - - lut_data->hue = pa_v2_config.pa_v2_data.global_hue_adj; - lut_data->sat = pa_v2_config.pa_v2_data.global_sat_adj; - lut_data->val = pa_v2_config.pa_v2_data.global_val_adj; - lut_data->cont = pa_v2_config.pa_v2_data.global_cont_adj; - } -} - static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) { u32 copyback = 0, copy_from_kernel = 1; @@ -298,22 +244,12 @@ static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; igc_config.len = IGC_LUT_ENTRIES; - igc_config.c0_c1_data = igc_inverted; - igc_config.c2_data = igc_rgb; + igc_config.c0_c1_data = &igc_Table_Inverted[0]; + igc_config.c2_data = &igc_Table_RGB[0]; mdss_mdp_igc_lut_config(&igc_config, ©back, copy_from_kernel); } -static void mdss_mdp_kcal_check_pcc(struct kcal_lut_data *lut_data) -{ - lut_data->red = lut_data->red < lut_data->minimum ? - lut_data->minimum : lut_data->red; - lut_data->green = lut_data->green < lut_data->minimum ? - lut_data->minimum : lut_data->green; - lut_data->blue = lut_data->blue < lut_data->minimum ? - lut_data->minimum : lut_data->blue; -} - static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -321,16 +257,14 @@ static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, struct kcal_lut_data *lut_data = dev_get_drvdata(dev); r = sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); - if ((r != 3) || (kcal_r < 1 || kcal_r > 256) || - (kcal_g < 1 || kcal_g > 256) || (kcal_b < 1 || kcal_b > 256)) + if ((r != 3) || (kcal_r < 0 || kcal_r > 256) || + (kcal_g < 0 || kcal_g > 256) || (kcal_b < 0 || kcal_b > 256)) return -EINVAL; lut_data->red = kcal_r; lut_data->green = kcal_g; lut_data->blue = kcal_b; - mdss_mdp_kcal_check_pcc(lut_data); - if (mdss_mdp_kcal_is_panel_on()) mdss_mdp_kcal_update_pcc(lut_data); else @@ -344,9 +278,6 @@ static ssize_t kcal_show(struct device *dev, struct device_attribute *attr, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) - mdss_mdp_kcal_read_pcc(lut_data); - return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", lut_data->red, lut_data->green, lut_data->blue); } @@ -358,13 +289,11 @@ static ssize_t kcal_min_store(struct device *dev, struct kcal_lut_data *lut_data = dev_get_drvdata(dev); r = kstrtoint(buf, 10, &kcal_min); - if ((r) || (kcal_min < 1 || kcal_min > 256)) + if ((r) || (kcal_min < 0 || kcal_min > 256)) return -EINVAL; lut_data->minimum = kcal_min; - mdss_mdp_kcal_check_pcc(lut_data); - if (mdss_mdp_kcal_is_panel_on()) mdss_mdp_kcal_update_pcc(lut_data); else @@ -438,8 +367,6 @@ static ssize_t kcal_invert_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - /* IGC lut does not support reading regs in kernel space yet */ - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->invert); } @@ -468,9 +395,6 @@ static ssize_t kcal_sat_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) - mdss_mdp_kcal_read_pa(lut_data); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->sat); } @@ -499,9 +423,6 @@ static ssize_t kcal_hue_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) - mdss_mdp_kcal_read_pa(lut_data); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->hue); } @@ -530,9 +451,6 @@ static ssize_t kcal_val_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) - mdss_mdp_kcal_read_pa(lut_data); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->val); } @@ -561,9 +479,6 @@ static ssize_t kcal_cont_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - if (mdss_mdp_kcal_is_panel_on() && lut_data->enable) - mdss_mdp_kcal_read_pa(lut_data); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->cont); } From fa7a107f213ea1398fc0318e6712eaac446abcd3 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:42:22 +0100 Subject: [PATCH 114/296] Revert "msm: mdss: KCAL: Apply default values on boot" This reverts commit 371f383ed0b3b568ae160e90405b27f74d26c2c2. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 36 ++++++++------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index dfb88580ab184..d36d930054455 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -32,7 +32,7 @@ #include "mdss_mdp.h" -#define DEF_PCC 0x100 +#define NUM_QLUT 0x100 #define DEF_PA 0xff #define PCC_ADJ 0x80 @@ -532,7 +532,7 @@ static int kcal_ctrl_probe(struct platform_device *pdev) int ret; struct kcal_lut_data *lut_data; - lut_data = devm_kzalloc(&pdev->dev, sizeof(*lut_data), GFP_KERNEL); + lut_data = kzalloc(sizeof(*lut_data), GFP_KERNEL); if (!lut_data) { pr_err("%s: failed to allocate memory for lut_data\n", __func__); @@ -541,30 +541,24 @@ static int kcal_ctrl_probe(struct platform_device *pdev) platform_set_drvdata(pdev, lut_data); - lut_data->enable = 0x1; - lut_data->red = DEF_PCC; - lut_data->green = DEF_PCC; - lut_data->blue = DEF_PCC; - lut_data->minimum = 0x23; - lut_data->invert = 0x0; - lut_data->hue = 0x0; + lut_data->red = lut_data->green = lut_data->blue = NUM_QLUT; + lut_data->minimum = 35; + lut_data->enable = 1; + lut_data->invert = 0; lut_data->sat = DEF_PA; + lut_data->hue = 0; lut_data->val = DEF_PA; lut_data->cont = DEF_PA; lut_data->queue_changes = false; - mdss_mdp_kcal_update_pcc(lut_data); - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_update_igc(lut_data); - #if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) lut_data->panel_nb.display_on = mdss_mdp_kcal_update_queue; lut_data->panel_nb.dev = &pdev->dev; ret = mmi_panel_register_notifier(&lut_data->panel_nb); if (ret) { pr_err("%s: unable to register MMI notifier\n", __func__); - return ret; + goto out_free_mem; } #elif defined(CONFIG_FB) lut_data->dev = pdev->dev; @@ -572,7 +566,7 @@ static int kcal_ctrl_probe(struct platform_device *pdev) ret = fb_register_client(&lut_data->panel_nb); if (ret) { pr_err("%s: unable to register fb notifier\n", __func__); - return ret; + goto out_free_mem; } #endif @@ -586,17 +580,13 @@ static int kcal_ctrl_probe(struct platform_device *pdev) ret |= device_create_file(&pdev->dev, &dev_attr_kcal_cont); if (ret) { pr_err("%s: unable to create sysfs entries\n", __func__); - goto out_notifier; + goto out_free_mem; } return 0; -out_notifier: -#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) - mmi_panel_unregister_notifier(&lut_data->panel_nb); -#elif defined(CONFIG_FB) - fb_unregister_client(&lut_data->panel_nb); -#endif +out_free_mem: + kfree(lut_data); return ret; } @@ -619,6 +609,8 @@ static int kcal_ctrl_remove(struct platform_device *pdev) fb_unregister_client(&lut_data->panel_nb); #endif + kfree(lut_data); + return 0; } From c3e015f16c33526ce035f9694fa0762ced28e7e9 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:43:03 +0100 Subject: [PATCH 115/296] Revert "msm: mdss: KCAL: Queue changes when panel is powered off" This reverts commit fa0b5ea05f0b50a31e8aedbf748241650809bb56. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 172 ++++++-------------- 1 file changed, 48 insertions(+), 124 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index d36d930054455..4bc7529d6df48 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -21,15 +21,6 @@ #include #include -#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) && defined(CONFIG_FB) -#include -#include -#include -#elif defined(CONFIG_FB) -#include -#include -#endif - #include "mdss_mdp.h" #define NUM_QLUT 0x100 @@ -37,13 +28,6 @@ #define PCC_ADJ 0x80 struct kcal_lut_data { -#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) && defined(CONFIG_FB) - struct mmi_notifier panel_nb; -#elif defined(CONFIG_FB) - struct device dev; - struct notifier_block panel_nb; -#endif - bool queue_changes; int red; int green; int blue; @@ -168,13 +152,6 @@ static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) u32 copyback = 0; struct mdp_pcc_cfg_data pcc_config; - lut_data->red = lut_data->red < lut_data->minimum ? - lut_data->minimum : lut_data->red; - lut_data->green = lut_data->green < lut_data->minimum ? - lut_data->minimum : lut_data->green; - lut_data->blue = lut_data->blue < lut_data->minimum ? - lut_data->minimum : lut_data->blue; - memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; @@ -250,6 +227,18 @@ static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) mdss_mdp_igc_lut_config(&igc_config, ©back, copy_from_kernel); } +static void kcal_apply_values(struct kcal_lut_data *lut_data) +{ + lut_data->red = lut_data->red < lut_data->minimum ? + lut_data->minimum : lut_data->red; + lut_data->green = lut_data->green < lut_data->minimum ? + lut_data->minimum : lut_data->green; + lut_data->blue = lut_data->blue < lut_data->minimum ? + lut_data->minimum : lut_data->blue; + + mdss_mdp_kcal_update_pcc(lut_data); +} + static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -261,14 +250,14 @@ static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, (kcal_g < 0 || kcal_g > 256) || (kcal_b < 0 || kcal_b > 256)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->red = kcal_r; lut_data->green = kcal_g; lut_data->blue = kcal_b; - if (mdss_mdp_kcal_is_panel_on()) - mdss_mdp_kcal_update_pcc(lut_data); - else - lut_data->queue_changes = true; + kcal_apply_values(lut_data); return count; } @@ -292,12 +281,12 @@ static ssize_t kcal_min_store(struct device *dev, if ((r) || (kcal_min < 0 || kcal_min > 256)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->minimum = kcal_min; - if (mdss_mdp_kcal_is_panel_on()) - mdss_mdp_kcal_update_pcc(lut_data); - else - lut_data->queue_changes = true; + kcal_apply_values(lut_data); return count; } @@ -321,14 +310,14 @@ static ssize_t kcal_enable_store(struct device *dev, (lut_data->enable == kcal_enable)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->enable = kcal_enable; - if (mdss_mdp_kcal_is_panel_on()) { - mdss_mdp_kcal_update_pcc(lut_data); - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_update_igc(lut_data); - } else - lut_data->queue_changes = true; + mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_kcal_update_igc(lut_data); return count; } @@ -352,12 +341,12 @@ static ssize_t kcal_invert_store(struct device *dev, (lut_data->invert == kcal_invert)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->invert = kcal_invert; - if (mdss_mdp_kcal_is_panel_on()) - mdss_mdp_kcal_update_igc(lut_data); - else - lut_data->queue_changes = true; + mdss_mdp_kcal_update_igc(lut_data); return count; } @@ -380,12 +369,12 @@ static ssize_t kcal_sat_store(struct device *dev, if ((r) || ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->sat = kcal_sat; - if (mdss_mdp_kcal_is_panel_on()) - mdss_mdp_kcal_update_pa(lut_data); - else - lut_data->queue_changes = true; + mdss_mdp_kcal_update_pa(lut_data); return count; } @@ -408,12 +397,12 @@ static ssize_t kcal_hue_store(struct device *dev, if ((r) || (kcal_hue < 0 || kcal_hue > 1536)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->hue = kcal_hue; - if (mdss_mdp_kcal_is_panel_on()) - mdss_mdp_kcal_update_pa(lut_data); - else - lut_data->queue_changes = true; + mdss_mdp_kcal_update_pa(lut_data); return count; } @@ -436,12 +425,12 @@ static ssize_t kcal_val_store(struct device *dev, if ((r) || (kcal_val < 128 || kcal_val > 383)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->val = kcal_val; - if (mdss_mdp_kcal_is_panel_on()) - mdss_mdp_kcal_update_pa(lut_data); - else - lut_data->queue_changes = true; + mdss_mdp_kcal_update_pa(lut_data); return count; } @@ -464,12 +453,12 @@ static ssize_t kcal_cont_store(struct device *dev, if ((r) || (kcal_cont < 128 || kcal_cont > 383)) return -EINVAL; + if (!mdss_mdp_kcal_is_panel_on()) + return -EPERM; + lut_data->cont = kcal_cont; - if (mdss_mdp_kcal_is_panel_on()) - mdss_mdp_kcal_update_pa(lut_data); - else - lut_data->queue_changes = true; + mdss_mdp_kcal_update_pa(lut_data); return count; } @@ -494,39 +483,6 @@ static DEVICE_ATTR(kcal_val, S_IWUSR | S_IRUGO, kcal_val_show, kcal_val_store); static DEVICE_ATTR(kcal_cont, S_IWUSR | S_IRUGO, kcal_cont_show, kcal_cont_store); -static int mdss_mdp_kcal_update_queue(struct device *dev) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (lut_data->queue_changes) { - mdss_mdp_kcal_update_pcc(lut_data); - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_update_igc(lut_data); - lut_data->queue_changes = false; - } - - return 0; -} - -#if defined(CONFIG_FB) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) -static int fb_notifier_callback(struct notifier_block *nb, - unsigned long event, void *data) -{ - int *blank; - struct fb_event *evdata = data; - struct kcal_lut_data *lut_data = - container_of(nb, struct kcal_lut_data, panel_nb); - - if (evdata && evdata->data && event == FB_EVENT_BLANK) { - blank = evdata->data; - if (*blank == FB_BLANK_UNBLANK) - mdss_mdp_kcal_update_queue(&lut_data->dev); - } - - return 0; -} -#endif - static int kcal_ctrl_probe(struct platform_device *pdev) { int ret; @@ -539,8 +495,6 @@ static int kcal_ctrl_probe(struct platform_device *pdev) return -ENOMEM; } - platform_set_drvdata(pdev, lut_data); - lut_data->red = lut_data->green = lut_data->blue = NUM_QLUT; lut_data->minimum = 35; lut_data->enable = 1; @@ -550,25 +504,7 @@ static int kcal_ctrl_probe(struct platform_device *pdev) lut_data->val = DEF_PA; lut_data->cont = DEF_PA; - lut_data->queue_changes = false; - -#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) - lut_data->panel_nb.display_on = mdss_mdp_kcal_update_queue; - lut_data->panel_nb.dev = &pdev->dev; - ret = mmi_panel_register_notifier(&lut_data->panel_nb); - if (ret) { - pr_err("%s: unable to register MMI notifier\n", __func__); - goto out_free_mem; - } -#elif defined(CONFIG_FB) - lut_data->dev = pdev->dev; - lut_data->panel_nb.notifier_call = fb_notifier_callback; - ret = fb_register_client(&lut_data->panel_nb); - if (ret) { - pr_err("%s: unable to register fb notifier\n", __func__); - goto out_free_mem; - } -#endif + platform_set_drvdata(pdev, lut_data); ret = device_create_file(&pdev->dev, &dev_attr_kcal); ret |= device_create_file(&pdev->dev, &dev_attr_kcal_min); @@ -578,15 +514,9 @@ static int kcal_ctrl_probe(struct platform_device *pdev) ret |= device_create_file(&pdev->dev, &dev_attr_kcal_hue); ret |= device_create_file(&pdev->dev, &dev_attr_kcal_val); ret |= device_create_file(&pdev->dev, &dev_attr_kcal_cont); - if (ret) { + if (ret) pr_err("%s: unable to create sysfs entries\n", __func__); - goto out_free_mem; - } - - return 0; -out_free_mem: - kfree(lut_data); return ret; } @@ -603,12 +533,6 @@ static int kcal_ctrl_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_kcal_val); device_remove_file(&pdev->dev, &dev_attr_kcal_cont); -#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) - mmi_panel_unregister_notifier(&lut_data->panel_nb); -#elif defined(CONFIG_FB) - fb_unregister_client(&lut_data->panel_nb); -#endif - kfree(lut_data); return 0; From 25f1cd96a1df4e7e2ddfdae81220f910865c27b2 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:43:33 +0100 Subject: [PATCH 116/296] Revert "msm: mdss: KCAL: Remove MODULE_DESCRIPTION" This reverts commit f0f8c587a998a1c27dcf11192a9bd3e5c65c50d1. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 4bc7529d6df48..9a568b636b0c6 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -477,7 +477,7 @@ static DEVICE_ATTR(kcal_enable, S_IWUSR | S_IRUGO, kcal_enable_show, kcal_enable_store); static DEVICE_ATTR(kcal_invert, S_IWUSR | S_IRUGO, kcal_invert_show, kcal_invert_store); -static DEVICE_ATTR(kcal_sat, S_IWUSR | S_IRUGO, kcal_sat_show, kcal_sat_store); +static DEVICE_ATTR(kcal_sat, S_IWUSR | S_IRUGO, kcal_sat_show, kcal_sat_store); static DEVICE_ATTR(kcal_hue, S_IWUSR | S_IRUGO, kcal_hue_show, kcal_hue_store); static DEVICE_ATTR(kcal_val, S_IWUSR | S_IRUGO, kcal_val_show, kcal_val_store); static DEVICE_ATTR(kcal_cont, S_IWUSR | S_IRUGO, kcal_cont_show, @@ -571,3 +571,5 @@ static void __exit kcal_ctrl_exit(void) late_initcall(kcal_ctrl_init); module_exit(kcal_ctrl_exit); + +MODULE_DESCRIPTION("MDSS-MDP5 KCAL Driver"); From 5441829d24c7fbd228689f5e6bcaa62e631e98e7 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:44:00 +0100 Subject: [PATCH 117/296] Revert "msm: mdss: KCAL: Update Kconfig description" This reverts commit 93f7555881106a65a0f8fa9568ff74a8af52cd36. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig index c9eae28b745d3..58f2b940877c7 100644 --- a/drivers/video/msm/mdss/Kconfig +++ b/drivers/video/msm/mdss/Kconfig @@ -68,5 +68,5 @@ config FB_MSM_MDSS_KCAL_CTRL bool "MDSS color control" default y ---help--- - Enable sysfs for post-processing control of mdss-mdp5 display - controllers in MDSS. + Enable sysfs for rgb/lut control for mdss-mdp display controllers + in the MDSS sub-system. From ab2f460367fcc50ddd809421419fc8881deb6655 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:45:21 +0100 Subject: [PATCH 118/296] Revert "msm: mdss: KCAL: Update according to linux guidelines and checkpatch.pl" This reverts commit 5cef770f1248f48cfc8d9dbdc0d4581526164f9c. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 194 ++++++++++++-------- 1 file changed, 114 insertions(+), 80 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 9a568b636b0c6..2ed8711fe67fe 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -28,16 +28,16 @@ #define PCC_ADJ 0x80 struct kcal_lut_data { - int red; - int green; - int blue; - int minimum; - int enable; - int invert; - int sat; - int hue; - int val; - int cont; + int red; + int green; + int blue; + int minimum; + int enable; + int invert; + int sat; + int hue; + int val; + int cont; }; static uint32_t igc_Table_Inverted[IGC_LUT_ENTRIES] = { @@ -155,9 +155,8 @@ static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pcc_config.ops = lut_data->enable ? - MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pcc_config.ops = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; pcc_config.r.r = lut_data->red * PCC_ADJ; pcc_config.g.g = lut_data->green * PCC_ADJ; pcc_config.b.b = lut_data->blue * PCC_ADJ; @@ -176,9 +175,8 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_config.pa_data.flags = lut_data->enable ? - MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pa_config.pa_data.flags = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; pa_config.pa_data.hue_adj = lut_data->hue; pa_config.pa_data.sat_adj = lut_data->sat; pa_config.pa_data.val_adj = lut_data->val; @@ -189,9 +187,8 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_v2_config.pa_v2_data.flags = lut_data->enable ? - MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pa_v2_config.pa_v2_data.flags = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_MASK; pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; @@ -217,9 +214,8 @@ static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) memset(&igc_config, 0, sizeof(struct mdp_igc_lut_data)); igc_config.block = MDP_LOGICAL_BLOCK_DISP_0; - igc_config.ops = lut_data->invert && lut_data->enable ? - MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + igc_config.ops = lut_data->invert && lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; igc_config.len = IGC_LUT_ENTRIES; igc_config.c0_c1_data = &igc_Table_Inverted[0]; igc_config.c2_data = &igc_Table_RGB[0]; @@ -229,11 +225,11 @@ static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) static void kcal_apply_values(struct kcal_lut_data *lut_data) { - lut_data->red = lut_data->red < lut_data->minimum ? + lut_data->red = (lut_data->red < lut_data->minimum) ? lut_data->minimum : lut_data->red; - lut_data->green = lut_data->green < lut_data->minimum ? + lut_data->green = (lut_data->green < lut_data->minimum) ? lut_data->minimum : lut_data->green; - lut_data->blue = lut_data->blue < lut_data->minimum ? + lut_data->blue = (lut_data->blue < lut_data->minimum) ? lut_data->minimum : lut_data->blue; mdss_mdp_kcal_update_pcc(lut_data); @@ -242,16 +238,25 @@ static void kcal_apply_values(struct kcal_lut_data *lut_data) static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_r, kcal_g, kcal_b, r; + int kcal_r, kcal_g, kcal_b; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); - if ((r != 3) || (kcal_r < 0 || kcal_r > 256) || - (kcal_g < 0 || kcal_g > 256) || (kcal_b < 0 || kcal_b > 256)) + if (count > 12) + return -EINVAL; + + sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); + + if (kcal_r < 0 || kcal_r > 256) + return -EINVAL; + + if (kcal_g < 0 || kcal_g > 256) + return -EINVAL; + + if (kcal_b < 0 || kcal_b > 256) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->red = kcal_r; lut_data->green = kcal_g; @@ -267,22 +272,26 @@ static ssize_t kcal_show(struct device *dev, struct device_attribute *attr, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", - lut_data->red, lut_data->green, lut_data->blue); + return sprintf(buf, "%d %d %d\n", lut_data->red, lut_data->green, + lut_data->blue); } static ssize_t kcal_min_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_min, r; + int kcal_min; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = kstrtoint(buf, 10, &kcal_min); - if ((r) || (kcal_min < 0 || kcal_min > 256)) + if (count > 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_min); + + if (kcal_min < 0 || kcal_min > 256) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->minimum = kcal_min; @@ -296,22 +305,28 @@ static ssize_t kcal_min_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->minimum); + return sprintf(buf, "%d\n", lut_data->minimum); } static ssize_t kcal_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_enable, r; + int kcal_enable; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = kstrtoint(buf, 10, &kcal_enable); - if ((r) || (kcal_enable != 0 && kcal_enable != 1) || - (lut_data->enable == kcal_enable)) + if (count != 2) + return -EINVAL; + + sscanf(buf, "%d", &kcal_enable); + + if (kcal_enable != 0 && kcal_enable != 1) + return -EINVAL; + + if (lut_data->enable == kcal_enable) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->enable = kcal_enable; @@ -327,22 +342,28 @@ static ssize_t kcal_enable_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->enable); + return sprintf(buf, "%d\n", lut_data->enable); } static ssize_t kcal_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_invert, r; + int kcal_invert; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = kstrtoint(buf, 10, &kcal_invert); - if ((r) || (kcal_invert != 0 && kcal_invert != 1) || - (lut_data->invert == kcal_invert)) + if (count != 2) + return -EINVAL; + + sscanf(buf, "%d", &kcal_invert); + + if (kcal_invert != 0 && kcal_invert != 1) + return -EINVAL; + + if (lut_data->invert == kcal_invert) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->invert = kcal_invert; @@ -356,21 +377,25 @@ static ssize_t kcal_invert_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->invert); + return sprintf(buf, "%d\n", lut_data->invert); } static ssize_t kcal_sat_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_sat, r; + int kcal_sat; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = kstrtoint(buf, 10, &kcal_sat); - if ((r) || ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128)) + if (count != 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_sat); + + if ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->sat = kcal_sat; @@ -384,21 +409,25 @@ static ssize_t kcal_sat_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->sat); + return sprintf(buf, "%d\n", lut_data->sat); } static ssize_t kcal_hue_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_hue, r; + int kcal_hue; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = kstrtoint(buf, 10, &kcal_hue); - if ((r) || (kcal_hue < 0 || kcal_hue > 1536)) + if (count > 5) + return -EINVAL; + + sscanf(buf, "%d", &kcal_hue); + + if (kcal_hue < 0 || kcal_hue > 1536) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->hue = kcal_hue; @@ -412,21 +441,25 @@ static ssize_t kcal_hue_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->hue); + return sprintf(buf, "%d\n", lut_data->hue); } static ssize_t kcal_val_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_val, r; + int kcal_val; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = kstrtoint(buf, 10, &kcal_val); - if ((r) || (kcal_val < 128 || kcal_val > 383)) + if (count != 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_val); + + if (kcal_val < 128 || kcal_val > 383) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->val = kcal_val; @@ -440,21 +473,25 @@ static ssize_t kcal_val_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->val); + return sprintf(buf, "%d\n", lut_data->val); } static ssize_t kcal_cont_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int kcal_cont, r; + int kcal_cont; struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - r = kstrtoint(buf, 10, &kcal_cont); - if ((r) || (kcal_cont < 128 || kcal_cont > 383)) + if (count != 4) + return -EINVAL; + + sscanf(buf, "%d", &kcal_cont); + + if (kcal_cont < 128 || kcal_cont > 383) return -EINVAL; if (!mdss_mdp_kcal_is_panel_on()) - return -EPERM; + return -EINVAL; lut_data->cont = kcal_cont; @@ -468,20 +505,17 @@ static ssize_t kcal_cont_show(struct device *dev, { struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", lut_data->cont); + return sprintf(buf, "%d\n", lut_data->cont); } -static DEVICE_ATTR(kcal, S_IWUSR | S_IRUGO, kcal_show, kcal_store); -static DEVICE_ATTR(kcal_min, S_IWUSR | S_IRUGO, kcal_min_show, kcal_min_store); -static DEVICE_ATTR(kcal_enable, S_IWUSR | S_IRUGO, kcal_enable_show, - kcal_enable_store); -static DEVICE_ATTR(kcal_invert, S_IWUSR | S_IRUGO, kcal_invert_show, - kcal_invert_store); -static DEVICE_ATTR(kcal_sat, S_IWUSR | S_IRUGO, kcal_sat_show, kcal_sat_store); -static DEVICE_ATTR(kcal_hue, S_IWUSR | S_IRUGO, kcal_hue_show, kcal_hue_store); -static DEVICE_ATTR(kcal_val, S_IWUSR | S_IRUGO, kcal_val_show, kcal_val_store); -static DEVICE_ATTR(kcal_cont, S_IWUSR | S_IRUGO, kcal_cont_show, - kcal_cont_store); +static DEVICE_ATTR(kcal, 0644, kcal_show, kcal_store); +static DEVICE_ATTR(kcal_min, 0644, kcal_min_show, kcal_min_store); +static DEVICE_ATTR(kcal_enable, 0644, kcal_enable_show, kcal_enable_store); +static DEVICE_ATTR(kcal_invert, 0644, kcal_invert_show, kcal_invert_store); +static DEVICE_ATTR(kcal_sat, 0644, kcal_sat_show, kcal_sat_store); +static DEVICE_ATTR(kcal_hue, 0644, kcal_hue_show, kcal_hue_store); +static DEVICE_ATTR(kcal_val, 0644, kcal_val_show, kcal_val_store); +static DEVICE_ATTR(kcal_cont, 0644, kcal_cont_show, kcal_cont_store); static int kcal_ctrl_probe(struct platform_device *pdev) { From c14bf1ff144438dfcd876a6ac357006c7e5a4c01 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:45:50 +0100 Subject: [PATCH 119/296] Revert "msm: mdss: KCAL: Check panel power state before applying values" This reverts commit 70bd44ab7ff58d4f77c3adb8d7ef74133a994869. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 39 --------------------- 1 file changed, 39 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 2ed8711fe67fe..0e5f370afc2e0 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -132,21 +132,6 @@ static uint32_t igc_Table_RGB[IGC_LUT_ENTRIES] = { 48, 32, 16, 0 }; -static bool mdss_mdp_kcal_is_panel_on(void) -{ - int i; - struct mdss_mdp_ctl *ctl; - struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - - for (i = 0; i < mdata->nctl; i++) { - ctl = mdata->ctl_off + i; - if (mdss_mdp_ctl_is_power_on(ctl)) - return true; - } - - return false; -} - static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) { u32 copyback = 0; @@ -255,9 +240,6 @@ static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, if (kcal_b < 0 || kcal_b > 256) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->red = kcal_r; lut_data->green = kcal_g; lut_data->blue = kcal_b; @@ -290,9 +272,6 @@ static ssize_t kcal_min_store(struct device *dev, if (kcal_min < 0 || kcal_min > 256) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->minimum = kcal_min; kcal_apply_values(lut_data); @@ -325,9 +304,6 @@ static ssize_t kcal_enable_store(struct device *dev, if (lut_data->enable == kcal_enable) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->enable = kcal_enable; mdss_mdp_kcal_update_pcc(lut_data); @@ -362,9 +338,6 @@ static ssize_t kcal_invert_store(struct device *dev, if (lut_data->invert == kcal_invert) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->invert = kcal_invert; mdss_mdp_kcal_update_igc(lut_data); @@ -394,9 +367,6 @@ static ssize_t kcal_sat_store(struct device *dev, if ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->sat = kcal_sat; mdss_mdp_kcal_update_pa(lut_data); @@ -426,9 +396,6 @@ static ssize_t kcal_hue_store(struct device *dev, if (kcal_hue < 0 || kcal_hue > 1536) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->hue = kcal_hue; mdss_mdp_kcal_update_pa(lut_data); @@ -458,9 +425,6 @@ static ssize_t kcal_val_store(struct device *dev, if (kcal_val < 128 || kcal_val > 383) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->val = kcal_val; mdss_mdp_kcal_update_pa(lut_data); @@ -490,9 +454,6 @@ static ssize_t kcal_cont_store(struct device *dev, if (kcal_cont < 128 || kcal_cont > 383) return -EINVAL; - if (!mdss_mdp_kcal_is_panel_on()) - return -EINVAL; - lut_data->cont = kcal_cont; mdss_mdp_kcal_update_pa(lut_data); From 85860c04d9fe7fca0b72d779a2eb59665208defa Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:48:56 +0100 Subject: [PATCH 120/296] Revert "msm: mdss: KCAL: Allow kcal_enable to control all post-processing features" This reverts commit 7069726271f490b09e34715b6b34671938f13a19. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 30 +++++++++------------ 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 0e5f370afc2e0..2ac3dd260181b 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -132,7 +132,7 @@ static uint32_t igc_Table_RGB[IGC_LUT_ENTRIES] = { 48, 32, 16, 0 }; -static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) +static void mdss_mdp_pp_kcal_update(struct kcal_lut_data *lut_data) { u32 copyback = 0; struct mdp_pcc_cfg_data pcc_config; @@ -149,7 +149,7 @@ static void mdss_mdp_kcal_update_pcc(struct kcal_lut_data *lut_data) mdss_mdp_pcc_config(&pcc_config, ©back); } -static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) +static void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) { u32 copyback = 0; struct mdp_pa_cfg_data pa_config; @@ -160,8 +160,7 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_config.pa_data.flags = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; pa_config.pa_data.hue_adj = lut_data->hue; pa_config.pa_data.sat_adj = lut_data->sat; pa_config.pa_data.val_adj = lut_data->val; @@ -172,8 +171,7 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_v2_config.pa_v2_data.flags = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pa_v2_config.pa_v2_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_MASK; pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; @@ -191,7 +189,7 @@ static void mdss_mdp_kcal_update_pa(struct kcal_lut_data *lut_data) } } -static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) +static void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data) { u32 copyback = 0, copy_from_kernel = 1; struct mdp_igc_lut_data igc_config; @@ -199,7 +197,7 @@ static void mdss_mdp_kcal_update_igc(struct kcal_lut_data *lut_data) memset(&igc_config, 0, sizeof(struct mdp_igc_lut_data)); igc_config.block = MDP_LOGICAL_BLOCK_DISP_0; - igc_config.ops = lut_data->invert && lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + igc_config.ops = lut_data->invert ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; igc_config.len = IGC_LUT_ENTRIES; igc_config.c0_c1_data = &igc_Table_Inverted[0]; @@ -217,7 +215,7 @@ static void kcal_apply_values(struct kcal_lut_data *lut_data) lut_data->blue = (lut_data->blue < lut_data->minimum) ? lut_data->minimum : lut_data->blue; - mdss_mdp_kcal_update_pcc(lut_data); + mdss_mdp_pp_kcal_update(lut_data); } static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, @@ -306,9 +304,7 @@ static ssize_t kcal_enable_store(struct device *dev, lut_data->enable = kcal_enable; - mdss_mdp_kcal_update_pcc(lut_data); - mdss_mdp_kcal_update_pa(lut_data); - mdss_mdp_kcal_update_igc(lut_data); + mdss_mdp_pp_kcal_update(lut_data); return count; } @@ -340,7 +336,7 @@ static ssize_t kcal_invert_store(struct device *dev, lut_data->invert = kcal_invert; - mdss_mdp_kcal_update_igc(lut_data); + mdss_mdp_pp_kcal_invert(lut_data); return count; } @@ -369,7 +365,7 @@ static ssize_t kcal_sat_store(struct device *dev, lut_data->sat = kcal_sat; - mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_pp_kcal_pa(lut_data); return count; } @@ -398,7 +394,7 @@ static ssize_t kcal_hue_store(struct device *dev, lut_data->hue = kcal_hue; - mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_pp_kcal_pa(lut_data); return count; } @@ -427,7 +423,7 @@ static ssize_t kcal_val_store(struct device *dev, lut_data->val = kcal_val; - mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_pp_kcal_pa(lut_data); return count; } @@ -456,7 +452,7 @@ static ssize_t kcal_cont_store(struct device *dev, lut_data->cont = kcal_cont; - mdss_mdp_kcal_update_pa(lut_data); + mdss_mdp_pp_kcal_pa(lut_data); return count; } From ad1b2741dcb223f1b7e4e46efd6ee5b794c33285 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:49:33 +0100 Subject: [PATCH 121/296] Revert "msm: mdss: Keep KCAL within its own source file" This reverts commit c6fad76b486d0adffd0c330a133fac13aca069cb. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 189 +------------------- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h | 41 +++++ drivers/video/msm/mdss/mdss_mdp_pp.c | 119 ++++++++++++ 3 files changed, 165 insertions(+), 184 deletions(-) create mode 100644 drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 2ac3dd260181b..f696f32c2ec1a 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -22,192 +22,13 @@ #include #include "mdss_mdp.h" - -#define NUM_QLUT 0x100 -#define DEF_PA 0xff -#define PCC_ADJ 0x80 - -struct kcal_lut_data { - int red; - int green; - int blue; - int minimum; - int enable; - int invert; - int sat; - int hue; - int val; - int cont; -}; - -static uint32_t igc_Table_Inverted[IGC_LUT_ENTRIES] = { - 267390960, 266342368, 265293776, 264245184, - 263196592, 262148000, 261099408, 260050816, - 259002224, 257953632, 256905040, 255856448, - 254807856, 253759264, 252710672, 251662080, - 250613488, 249564896, 248516304, 247467712, - 246419120, 245370528, 244321936, 243273344, - 242224752, 241176160, 240127568, 239078976, - 238030384, 236981792, 235933200, 234884608, - 233836016, 232787424, 231738832, 230690240, - 229641648, 228593056, 227544464, 226495872, - 225447280, 224398688, 223350096, 222301504, - 221252912, 220204320, 219155728, 218107136, - 217058544, 216009952, 214961360, 213912768, - 212864176, 211815584, 210766992, 209718400, - 208669808, 207621216, 206572624, 205524032, - 204475440, 203426848, 202378256, 201329664, - 200281072, 199232480, 198183888, 197135296, - 196086704, 195038112, 193989520, 192940928, - 191892336, 190843744, 189795152, 188746560, - 187697968, 186649376, 185600784, 184552192, - 183503600, 182455008, 181406416, 180357824, - 179309232, 178260640, 177212048, 176163456, - 175114864, 174066272, 173017680, 171969088, - 170920496, 169871904, 168823312, 167774720, - 166726128, 165677536, 164628944, 163580352, - 162531760, 161483168, 160434576, 159385984, - 158337392, 157288800, 156240208, 155191616, - 154143024, 153094432, 152045840, 150997248, - 149948656, 148900064, 147851472, 146802880, - 145754288, 144705696, 143657104, 142608512, - 141559920, 140511328, 139462736, 138414144, - 137365552, 136316960, 135268368, 134219776, - 133171184, 132122592, 131074000, 130025408, - 128976816, 127928224, 126879632, 125831040, - 124782448, 123733856, 122685264, 121636672, - 120588080, 119539488, 118490896, 117442304, - 116393712, 115345120, 114296528, 113247936, - 112199344, 111150752, 110102160, 109053568, - 108004976, 106956384, 105907792, 104859200, - 103810608, 102762016, 101713424, 100664832, - 99616240, 98567648, 97519056, 96470464, - 95421872, 94373280, 93324688, 92276096, - 91227504, 90178912, 89130320, 88081728, - 87033136, 85984544, 84935952, 83887360, - 82838768, 81790176, 80741584, 79692992, - 78644400, 77595808, 76547216, 75498624, - 74450032, 73401440, 72352848, 71304256, - 70255664, 69207072, 68158480, 67109888, - 66061296, 65012704, 63964112, 62915520, - 61866928, 60818336, 59769744, 58721152, - 57672560, 56623968, 55575376, 54526784, - 53478192, 52429600, 51381008, 50332416, - 49283824, 48235232, 47186640, 46138048, - 45089456, 44040864, 42992272, 41943680, - 40895088, 39846496, 38797904, 37749312, - 36700720, 35652128, 34603536, 33554944, - 32506352, 31457760, 30409168, 29360576, - 28311984, 27263392, 26214800, 25166208, - 24117616, 23069024, 22020432, 20971840, - 19923248, 18874656, 17826064, 16777472, - 15728880, 14680288, 13631696, 12583104, - 11534512, 10485920, 9437328, 8388736, - 7340144, 6291552, 5242960, 4194368, - 3145776, 2097184, 1048592, 0 -}; - -static uint32_t igc_Table_RGB[IGC_LUT_ENTRIES] = { - 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, - 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, - 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, - 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, - 3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, - 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, - 2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, - 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, - 2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, - 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, - 2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, - 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, - 1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, - 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, - 1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, - 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, - 1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, - 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, - 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, - 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, - 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, - 48, 32, 16, 0 -}; - -static void mdss_mdp_pp_kcal_update(struct kcal_lut_data *lut_data) -{ - u32 copyback = 0; - struct mdp_pcc_cfg_data pcc_config; - - memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); - - pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pcc_config.ops = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; - pcc_config.r.r = lut_data->red * PCC_ADJ; - pcc_config.g.g = lut_data->green * PCC_ADJ; - pcc_config.b.b = lut_data->blue * PCC_ADJ; - - mdss_mdp_pcc_config(&pcc_config, ©back); -} - -static void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) -{ - u32 copyback = 0; - struct mdp_pa_cfg_data pa_config; - struct mdp_pa_v2_cfg_data pa_v2_config; - struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - - if (mdata->mdp_rev < MDSS_MDP_HW_REV_103) { - memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); - - pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pa_config.pa_data.hue_adj = lut_data->hue; - pa_config.pa_data.sat_adj = lut_data->sat; - pa_config.pa_data.val_adj = lut_data->val; - pa_config.pa_data.cont_adj = lut_data->cont; - - mdss_mdp_pa_config(&pa_config, ©back); - } else { - memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); - - pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_v2_config.pa_v2_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_MASK; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_MASK; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_MASK; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_MASK; - pa_v2_config.pa_v2_data.global_hue_adj = lut_data->hue; - pa_v2_config.pa_v2_data.global_sat_adj = lut_data->sat; - pa_v2_config.pa_v2_data.global_val_adj = lut_data->val; - pa_v2_config.pa_v2_data.global_cont_adj = lut_data->cont; - - mdss_mdp_pa_v2_config(&pa_v2_config, ©back); - } -} - -static void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data) -{ - u32 copyback = 0, copy_from_kernel = 1; - struct mdp_igc_lut_data igc_config; - - memset(&igc_config, 0, sizeof(struct mdp_igc_lut_data)); - - igc_config.block = MDP_LOGICAL_BLOCK_DISP_0; - igc_config.ops = lut_data->invert ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; - igc_config.len = IGC_LUT_ENTRIES; - igc_config.c0_c1_data = &igc_Table_Inverted[0]; - igc_config.c2_data = &igc_Table_RGB[0]; - - mdss_mdp_igc_lut_config(&igc_config, ©back, copy_from_kernel); -} +#include "mdss_mdp_kcal_ctrl.h" static void kcal_apply_values(struct kcal_lut_data *lut_data) { + /* gc_lut_* will save lut values even when disabled and + * properly restore them on enable. + */ lut_data->red = (lut_data->red < lut_data->minimum) ? lut_data->minimum : lut_data->red; lut_data->green = (lut_data->green < lut_data->minimum) ? @@ -563,4 +384,4 @@ static void __exit kcal_ctrl_exit(void) late_initcall(kcal_ctrl_init); module_exit(kcal_ctrl_exit); -MODULE_DESCRIPTION("MDSS-MDP5 KCAL Driver"); +MODULE_DESCRIPTION("LCD KCAL Driver"); diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h new file mode 100644 index 0000000000000..5517d3976d924 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h @@ -0,0 +1,41 @@ + +/* + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, LGE Inc. All rights reserved + * Copyright (c) 2014, savoca + * Copyright (c) 2014, Paul Reioux + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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_MDP_KCAL_CTRL_H +#define __MDSS_MDP_KCAL_CTRL_H + +#define NUM_QLUT 0x100 +#define DEF_PA 0xff +#define PCC_ADJ 0x80 + +struct kcal_lut_data { + int red; + int green; + int blue; + int minimum; + int enable; + int invert; + int sat; + int hue; + int val; + int cont; +}; + +void mdss_mdp_pp_kcal_update(struct kcal_lut_data *lut_data); +void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data); +void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data); +#endif diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index f6453a724a3d3..334f1940e165e 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -23,6 +23,8 @@ #include #include +#include "mdss_mdp_kcal_ctrl.h" + struct mdp_csc_cfg mdp_csc_convert[MDSS_MDP_MAX_CSC] = { [MDSS_MDP_CSC_YUV2RGB_601L] = { 0, @@ -398,6 +400,33 @@ struct mdss_pp_res_type { struct mdp_pcc_cfg_data user_pcc_disp_cfg[MDSS_BLOCK_DISP_NUM]; }; +uint32_t igc_Table_RGB[256] = { + 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, + 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, + 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, + 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, + 3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, + 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, + 2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, + 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, + 2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, + 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, + 2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, + 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, + 1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, + 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, + 1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, + 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, + 1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, + 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, + 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, + 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, + 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, + 48, 32, 16, 0 +}; +int igc_c0_c1[256] = {0,}; +int igc_c2[256] = {0,}; + static DEFINE_MUTEX(mdss_pp_mutex); static struct mdss_pp_res_type *mdss_pp_res; @@ -491,6 +520,7 @@ static int pp_ad_linearize_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out, static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, bool *bl_out_notify); static int pp_ad_shutdown_cleanup(struct msm_fb_data_type *mfd); +static struct msm_fb_data_type *mdss_get_mfd_from_index(int index); static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num); static inline bool pp_sts_is_enabled(u32 sts, int side); static inline void pp_sts_set_split_bits(u32 *sts, u32 bits); @@ -2133,6 +2163,95 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) return 0; } +void mdss_mdp_pp_kcal_update(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0; + struct mdp_pcc_cfg_data pcc_config; + + memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); + + pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pcc_config.ops = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : + MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + pcc_config.r.r = lut_data->red * PCC_ADJ; + pcc_config.g.g = lut_data->green * PCC_ADJ; + pcc_config.b.b = lut_data->blue * PCC_ADJ; + + mdss_mdp_pcc_config(&pcc_config, ©back); +} + +void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) +{ + u32 copyback = 0; + struct mdp_pa_cfg_data pa_config; + struct mdp_pa_v2_cfg_data pa_v2_config; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + if (mdata->mdp_rev < MDSS_MDP_HW_REV_103) { + memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); + + pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + pa_config.pa_data.hue_adj = lut_data->hue; + pa_config.pa_data.sat_adj = lut_data->sat; + pa_config.pa_data.val_adj = lut_data->val; + pa_config.pa_data.cont_adj = lut_data->cont; + + mdss_mdp_pa_config(&pa_config, ©back); + } else { + memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); + + pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_v2_config.pa_v2_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_MASK; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_MASK; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_MASK; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_ENABLE; + pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_MASK; + pa_v2_config.pa_v2_data.global_hue_adj = lut_data->hue; + pa_v2_config.pa_v2_data.global_sat_adj = lut_data->sat; + pa_v2_config.pa_v2_data.global_val_adj = lut_data->val; + pa_v2_config.pa_v2_data.global_cont_adj = lut_data->cont; + + mdss_mdp_pa_v2_config(&pa_v2_config, ©back); + } +} + +void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data) +{ + int i; + u32 disp_num = 0, copyback = 0, copy_from_kernel = 1; + struct msm_fb_data_type *igc_mfd; + struct mdp_igc_lut_data *igc_config; + + igc_mfd = mdss_get_mfd_from_index(0); + + igc_config = &mdss_pp_res->igc_disp_cfg[disp_num]; + igc_config->c0_c1_data = &mdss_pp_res->igc_lut_c0c1[disp_num][0]; + igc_config->c2_data = &mdss_pp_res->igc_lut_c2[disp_num][0]; + igc_config->block = MDP_LOGICAL_BLOCK_DISP_0; + igc_config->len = IGC_LUT_ENTRIES; + + if (igc_mfd && lut_data->invert) { + igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + for (i = 0; i < IGC_LUT_ENTRIES; i++) { + igc_c0_c1[i] = (igc_Table_RGB[i] & 0xfff) | + ((igc_Table_RGB[i] & 0xfff)) << 16; + igc_c2[i] = igc_Table_RGB[i]; + } + igc_config->c0_c1_data = &igc_c0_c1[0]; + igc_config->c2_data = &igc_c2[0]; + } else if (igc_mfd && !lut_data->invert) + igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + else + return; + + mdss_mdp_igc_lut_config(igc_config, ©back, copy_from_kernel); +} + int mdss_mdp_pp_init(struct device *dev) { int i, ret = 0; From 91c2960c8773447a1d5652a0a4502465419754ea Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:50:03 +0100 Subject: [PATCH 122/296] Revert "msm: mdss: Remove deprecated __dev* markings" This reverts commit 9896ad9a087b995d008849b1fd253dbe0a40b8a2. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index f696f32c2ec1a..058c84e22feb6 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -295,7 +295,7 @@ static DEVICE_ATTR(kcal_hue, 0644, kcal_hue_show, kcal_hue_store); static DEVICE_ATTR(kcal_val, 0644, kcal_val_show, kcal_val_store); static DEVICE_ATTR(kcal_cont, 0644, kcal_cont_show, kcal_cont_store); -static int kcal_ctrl_probe(struct platform_device *pdev) +static int __devinit kcal_ctrl_probe(struct platform_device *pdev) { int ret; struct kcal_lut_data *lut_data; @@ -332,7 +332,7 @@ static int kcal_ctrl_probe(struct platform_device *pdev) return ret; } -static int kcal_ctrl_remove(struct platform_device *pdev) +static int __devexit kcal_ctrl_remove(struct platform_device *pdev) { struct kcal_lut_data *lut_data = platform_get_drvdata(pdev); From 96747d93ab57f86c489a783e801ff2d2965248a4 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:50:33 +0100 Subject: [PATCH 123/296] Revert "msm: mdss: Add pa_v2 support to KCAL" This reverts commit 84c996b80d471a968ae1eae385fa0eaa8bcdb213. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_pp.c | 39 ++++++---------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index 334f1940e165e..2c758ca994dd6 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -2184,40 +2184,17 @@ void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) { u32 copyback = 0; struct mdp_pa_cfg_data pa_config; - struct mdp_pa_v2_cfg_data pa_v2_config; - struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - if (mdata->mdp_rev < MDSS_MDP_HW_REV_103) { - memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); + memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); - pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pa_config.pa_data.hue_adj = lut_data->hue; - pa_config.pa_data.sat_adj = lut_data->sat; - pa_config.pa_data.val_adj = lut_data->val; - pa_config.pa_data.cont_adj = lut_data->cont; + pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; + pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + pa_config.pa_data.sat_adj = lut_data->sat; + pa_config.pa_data.hue_adj = lut_data->hue; + pa_config.pa_data.val_adj = lut_data->val; + pa_config.pa_data.cont_adj = lut_data->cont; - mdss_mdp_pa_config(&pa_config, ©back); - } else { - memset(&pa_v2_config, 0, sizeof(struct mdp_pa_v2_cfg_data)); - - pa_v2_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_v2_config.pa_v2_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_HUE_MASK; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_SAT_MASK; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_VAL_MASK; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_ENABLE; - pa_v2_config.pa_v2_data.flags |= MDP_PP_PA_CONT_MASK; - pa_v2_config.pa_v2_data.global_hue_adj = lut_data->hue; - pa_v2_config.pa_v2_data.global_sat_adj = lut_data->sat; - pa_v2_config.pa_v2_data.global_val_adj = lut_data->val; - pa_v2_config.pa_v2_data.global_cont_adj = lut_data->cont; - - mdss_mdp_pa_v2_config(&pa_v2_config, ©back); - } + mdss_mdp_pa_config(&pa_config, ©back); } void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data) From 068e29f8ad689c549d8830abf3de63423c1135f8 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:51:02 +0100 Subject: [PATCH 124/296] Revert "msm: mdss: Replace PGC implementation with PCC for KCAL" This reverts commit b6b8744e5ddc8058510c77180ea432c1a13dfeb2. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 10 ++-- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h | 6 +-- drivers/video/msm/mdss/mdss_mdp_pp.c | 53 +++++++++++++++------ 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c index 058c84e22feb6..e9da0516b3ad7 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c @@ -36,7 +36,7 @@ static void kcal_apply_values(struct kcal_lut_data *lut_data) lut_data->blue = (lut_data->blue < lut_data->minimum) ? lut_data->minimum : lut_data->blue; - mdss_mdp_pp_kcal_update(lut_data); + mdss_mdp_pp_kcal_update(lut_data->red, lut_data->green, lut_data->blue); } static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, @@ -125,7 +125,7 @@ static ssize_t kcal_enable_store(struct device *dev, lut_data->enable = kcal_enable; - mdss_mdp_pp_kcal_update(lut_data); + mdss_mdp_pp_kcal_enable(lut_data->enable ? true : false); return count; } @@ -157,7 +157,7 @@ static ssize_t kcal_invert_store(struct device *dev, lut_data->invert = kcal_invert; - mdss_mdp_pp_kcal_invert(lut_data); + mdss_mdp_pp_kcal_invert(lut_data->invert); return count; } @@ -307,6 +307,10 @@ static int __devinit kcal_ctrl_probe(struct platform_device *pdev) return -ENOMEM; } + mdss_mdp_pp_kcal_enable(true); + + mdss_mdp_pp_kcal_update(NUM_QLUT, NUM_QLUT, NUM_QLUT); + lut_data->red = lut_data->green = lut_data->blue = NUM_QLUT; lut_data->minimum = 35; lut_data->enable = 1; diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h index 5517d3976d924..5ba35e0ac7d3a 100644 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h +++ b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h @@ -20,7 +20,6 @@ #define NUM_QLUT 0x100 #define DEF_PA 0xff -#define PCC_ADJ 0x80 struct kcal_lut_data { int red; @@ -35,7 +34,8 @@ struct kcal_lut_data { int cont; }; -void mdss_mdp_pp_kcal_update(struct kcal_lut_data *lut_data); +void mdss_mdp_pp_kcal_enable(bool enable); +void mdss_mdp_pp_kcal_update(int kr, int kg, int kb); void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data); -void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data); +void mdss_mdp_pp_kcal_invert(int enable); #endif diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index 2c758ca994dd6..ebad99e109f07 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -2112,6 +2112,10 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) mdss_pp_res->gamut_disp_cfg[disp_num].flags |= MDP_PP_OPS_WRITE; } + + if (!disp_num) + pp_sts.pgc_sts |= PP_STS_ENABLE; + if (pp_sts.pgc_sts & PP_STS_ENABLE) { flags |= PP_FLAGS_DIRTY_PGC; if (!(mdss_pp_res->pgc_disp_cfg[disp_num].flags @@ -2163,21 +2167,42 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) return 0; } -void mdss_mdp_pp_kcal_update(struct kcal_lut_data *lut_data) +void mdss_mdp_pp_kcal_enable(bool enable) { - u32 copyback = 0; - struct mdp_pcc_cfg_data pcc_config; + u32 disp_num = 0; + struct mdp_pgc_lut_data *pgc_config; - memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); + pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; + pgc_config->block = MDP_LOGICAL_BLOCK_DISP_0; + + if (enable) { + pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; + pgc_config->r_data = &mdss_pp_res->gc_lut_r[disp_num][0]; + pgc_config->g_data = &mdss_pp_res->gc_lut_g[disp_num][0]; + pgc_config->b_data = &mdss_pp_res->gc_lut_b[disp_num][0]; + } else + pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; + + mdss_pp_res->pgc_disp_cfg[disp_num] = *pgc_config; + mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PGC; +} - pcc_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pcc_config.ops = lut_data->enable ? MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE : - MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; - pcc_config.r.r = lut_data->red * PCC_ADJ; - pcc_config.g.g = lut_data->green * PCC_ADJ; - pcc_config.b.b = lut_data->blue * PCC_ADJ; +void mdss_mdp_pp_kcal_update(int kr, int kg, int kb) +{ + int i; + u32 disp_num = 0; + struct mdp_pgc_lut_data *pgc_config; + + pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; + + for (i = 0; i < GC_LUT_SEGMENTS; i++) { + pgc_config->r_data[i].slope = kr; + pgc_config->g_data[i].slope = kg; + pgc_config->b_data[i].slope = kb; + } - mdss_mdp_pcc_config(&pcc_config, ©back); + mdss_pp_res->pgc_disp_cfg[disp_num] = *pgc_config; + mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PGC; } void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) @@ -2197,7 +2222,7 @@ void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) mdss_mdp_pa_config(&pa_config, ©back); } -void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data) +void mdss_mdp_pp_kcal_invert(int enable) { int i; u32 disp_num = 0, copyback = 0, copy_from_kernel = 1; @@ -2212,7 +2237,7 @@ void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data) igc_config->block = MDP_LOGICAL_BLOCK_DISP_0; igc_config->len = IGC_LUT_ENTRIES; - if (igc_mfd && lut_data->invert) { + if (igc_mfd && enable) { igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; for (i = 0; i < IGC_LUT_ENTRIES; i++) { igc_c0_c1[i] = (igc_Table_RGB[i] & 0xfff) | @@ -2221,7 +2246,7 @@ void mdss_mdp_pp_kcal_invert(struct kcal_lut_data *lut_data) } igc_config->c0_c1_data = &igc_c0_c1[0]; igc_config->c2_data = &igc_c2[0]; - } else if (igc_mfd && !lut_data->invert) + } else if (igc_mfd && !enable) igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; else return; From 8d1280acb3aba01265f29c3fbc1737b3e57b85a6 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:51:36 +0100 Subject: [PATCH 125/296] Revert "msm: mdss: Add KCAL support for post processing control [v2]" This reverts commit ccd1f302d41101c69eb48c4cf96d8c153684a609. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/Kconfig | 8 - drivers/video/msm/mdss/Makefile | 2 - drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c | 391 -------------------- drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h | 41 -- drivers/video/msm/mdss/mdss_mdp_pp.c | 121 ------ 5 files changed, 563 deletions(-) delete mode 100644 drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c delete mode 100644 drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig index 58f2b940877c7..3029d0ef7498b 100644 --- a/drivers/video/msm/mdss/Kconfig +++ b/drivers/video/msm/mdss/Kconfig @@ -62,11 +62,3 @@ config FB_MSM_MDSS_XLOG_DEBUG features to: Dump MDSS registers during driver errors, panic driver during fatal errors and enable some display-driver logging into an internal buffer (this avoids logging overhead). - -config FB_MSM_MDSS_KCAL_CTRL - depends on FB_MSM_MDSS - bool "MDSS color control" - default y - ---help--- - Enable sysfs for rgb/lut control for mdss-mdp display controllers - in the MDSS sub-system. diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile index f0a76d813955b..c90b04ec0c75c 100644 --- a/drivers/video/msm/mdss/Makefile +++ b/drivers/video/msm/mdss/Makefile @@ -54,5 +54,3 @@ obj-$(CONFIG_COMPAT) += mdss_compat_utils.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_livedisplay.o obj-$(CONFIG_FB_MSM_MDSS) += lcd_notify.o - -obj-$(CONFIG_FB_MSM_MDSS_KCAL_CTRL) += mdss_mdp_kcal_ctrl.o diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c deleted file mode 100644 index e9da0516b3ad7..0000000000000 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. - * Copyright (c) 2013, LGE Inc. All rights reserved - * Copyright (c) 2014 savoca - * Copyright (c) 2014 Paul Reioux - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 "mdss_mdp.h" -#include "mdss_mdp_kcal_ctrl.h" - -static void kcal_apply_values(struct kcal_lut_data *lut_data) -{ - /* gc_lut_* will save lut values even when disabled and - * properly restore them on enable. - */ - lut_data->red = (lut_data->red < lut_data->minimum) ? - lut_data->minimum : lut_data->red; - lut_data->green = (lut_data->green < lut_data->minimum) ? - lut_data->minimum : lut_data->green; - lut_data->blue = (lut_data->blue < lut_data->minimum) ? - lut_data->minimum : lut_data->blue; - - mdss_mdp_pp_kcal_update(lut_data->red, lut_data->green, lut_data->blue); -} - -static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int kcal_r, kcal_g, kcal_b; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count > 12) - return -EINVAL; - - sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); - - if (kcal_r < 0 || kcal_r > 256) - return -EINVAL; - - if (kcal_g < 0 || kcal_g > 256) - return -EINVAL; - - if (kcal_b < 0 || kcal_b > 256) - return -EINVAL; - - lut_data->red = kcal_r; - lut_data->green = kcal_g; - lut_data->blue = kcal_b; - - kcal_apply_values(lut_data); - - return count; -} - -static ssize_t kcal_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d %d %d\n", lut_data->red, lut_data->green, - lut_data->blue); -} - -static ssize_t kcal_min_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int kcal_min; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count > 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_min); - - if (kcal_min < 0 || kcal_min > 256) - return -EINVAL; - - lut_data->minimum = kcal_min; - - kcal_apply_values(lut_data); - - return count; -} - -static ssize_t kcal_min_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", lut_data->minimum); -} - -static ssize_t kcal_enable_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int kcal_enable; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count != 2) - return -EINVAL; - - sscanf(buf, "%d", &kcal_enable); - - if (kcal_enable != 0 && kcal_enable != 1) - return -EINVAL; - - if (lut_data->enable == kcal_enable) - return -EINVAL; - - lut_data->enable = kcal_enable; - - mdss_mdp_pp_kcal_enable(lut_data->enable ? true : false); - - return count; -} - -static ssize_t kcal_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", lut_data->enable); -} - -static ssize_t kcal_invert_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int kcal_invert; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count != 2) - return -EINVAL; - - sscanf(buf, "%d", &kcal_invert); - - if (kcal_invert != 0 && kcal_invert != 1) - return -EINVAL; - - if (lut_data->invert == kcal_invert) - return -EINVAL; - - lut_data->invert = kcal_invert; - - mdss_mdp_pp_kcal_invert(lut_data->invert); - - return count; -} - -static ssize_t kcal_invert_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", lut_data->invert); -} - -static ssize_t kcal_sat_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int kcal_sat; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count != 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_sat); - - if ((kcal_sat < 224 || kcal_sat > 383) && kcal_sat != 128) - return -EINVAL; - - lut_data->sat = kcal_sat; - - mdss_mdp_pp_kcal_pa(lut_data); - - return count; -} - -static ssize_t kcal_sat_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", lut_data->sat); -} - -static ssize_t kcal_hue_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int kcal_hue; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count > 5) - return -EINVAL; - - sscanf(buf, "%d", &kcal_hue); - - if (kcal_hue < 0 || kcal_hue > 1536) - return -EINVAL; - - lut_data->hue = kcal_hue; - - mdss_mdp_pp_kcal_pa(lut_data); - - return count; -} - -static ssize_t kcal_hue_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", lut_data->hue); -} - -static ssize_t kcal_val_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int kcal_val; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count != 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_val); - - if (kcal_val < 128 || kcal_val > 383) - return -EINVAL; - - lut_data->val = kcal_val; - - mdss_mdp_pp_kcal_pa(lut_data); - - return count; -} - -static ssize_t kcal_val_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", lut_data->val); -} - -static ssize_t kcal_cont_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int kcal_cont; - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - if (count != 4) - return -EINVAL; - - sscanf(buf, "%d", &kcal_cont); - - if (kcal_cont < 128 || kcal_cont > 383) - return -EINVAL; - - lut_data->cont = kcal_cont; - - mdss_mdp_pp_kcal_pa(lut_data); - - return count; -} - -static ssize_t kcal_cont_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kcal_lut_data *lut_data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", lut_data->cont); -} - -static DEVICE_ATTR(kcal, 0644, kcal_show, kcal_store); -static DEVICE_ATTR(kcal_min, 0644, kcal_min_show, kcal_min_store); -static DEVICE_ATTR(kcal_enable, 0644, kcal_enable_show, kcal_enable_store); -static DEVICE_ATTR(kcal_invert, 0644, kcal_invert_show, kcal_invert_store); -static DEVICE_ATTR(kcal_sat, 0644, kcal_sat_show, kcal_sat_store); -static DEVICE_ATTR(kcal_hue, 0644, kcal_hue_show, kcal_hue_store); -static DEVICE_ATTR(kcal_val, 0644, kcal_val_show, kcal_val_store); -static DEVICE_ATTR(kcal_cont, 0644, kcal_cont_show, kcal_cont_store); - -static int __devinit kcal_ctrl_probe(struct platform_device *pdev) -{ - int ret; - struct kcal_lut_data *lut_data; - - lut_data = kzalloc(sizeof(*lut_data), GFP_KERNEL); - if (!lut_data) { - pr_err("%s: failed to allocate memory for lut_data\n", - __func__); - return -ENOMEM; - } - - mdss_mdp_pp_kcal_enable(true); - - mdss_mdp_pp_kcal_update(NUM_QLUT, NUM_QLUT, NUM_QLUT); - - lut_data->red = lut_data->green = lut_data->blue = NUM_QLUT; - lut_data->minimum = 35; - lut_data->enable = 1; - lut_data->invert = 0; - lut_data->sat = DEF_PA; - lut_data->hue = 0; - lut_data->val = DEF_PA; - lut_data->cont = DEF_PA; - - platform_set_drvdata(pdev, lut_data); - - ret = device_create_file(&pdev->dev, &dev_attr_kcal); - ret |= device_create_file(&pdev->dev, &dev_attr_kcal_min); - ret |= device_create_file(&pdev->dev, &dev_attr_kcal_enable); - ret |= device_create_file(&pdev->dev, &dev_attr_kcal_invert); - ret |= device_create_file(&pdev->dev, &dev_attr_kcal_sat); - ret |= device_create_file(&pdev->dev, &dev_attr_kcal_hue); - ret |= device_create_file(&pdev->dev, &dev_attr_kcal_val); - ret |= device_create_file(&pdev->dev, &dev_attr_kcal_cont); - if (ret) - pr_err("%s: unable to create sysfs entries\n", __func__); - - return ret; -} - -static int __devexit kcal_ctrl_remove(struct platform_device *pdev) -{ - struct kcal_lut_data *lut_data = platform_get_drvdata(pdev); - - device_remove_file(&pdev->dev, &dev_attr_kcal); - device_remove_file(&pdev->dev, &dev_attr_kcal_min); - device_remove_file(&pdev->dev, &dev_attr_kcal_enable); - device_remove_file(&pdev->dev, &dev_attr_kcal_invert); - device_remove_file(&pdev->dev, &dev_attr_kcal_sat); - device_remove_file(&pdev->dev, &dev_attr_kcal_hue); - device_remove_file(&pdev->dev, &dev_attr_kcal_val); - device_remove_file(&pdev->dev, &dev_attr_kcal_cont); - - kfree(lut_data); - - return 0; -} - -static struct platform_driver kcal_ctrl_driver = { - .probe = kcal_ctrl_probe, - .remove = kcal_ctrl_remove, - .driver = { - .name = "kcal_ctrl", - }, -}; - -static struct platform_device kcal_ctrl_device = { - .name = "kcal_ctrl", -}; - -static int __init kcal_ctrl_init(void) -{ - if (platform_driver_register(&kcal_ctrl_driver)) - return -ENODEV; - - if (platform_device_register(&kcal_ctrl_device)) - return -ENODEV; - - pr_info("%s: registered\n", __func__); - - return 0; -} - -static void __exit kcal_ctrl_exit(void) -{ - platform_device_unregister(&kcal_ctrl_device); - platform_driver_unregister(&kcal_ctrl_driver); -} - -late_initcall(kcal_ctrl_init); -module_exit(kcal_ctrl_exit); - -MODULE_DESCRIPTION("LCD KCAL Driver"); diff --git a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h b/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h deleted file mode 100644 index 5ba35e0ac7d3a..0000000000000 --- a/drivers/video/msm/mdss/mdss_mdp_kcal_ctrl.h +++ /dev/null @@ -1,41 +0,0 @@ - -/* - * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. - * Copyright (c) 2013, LGE Inc. All rights reserved - * Copyright (c) 2014, savoca - * Copyright (c) 2014, Paul Reioux - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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_MDP_KCAL_CTRL_H -#define __MDSS_MDP_KCAL_CTRL_H - -#define NUM_QLUT 0x100 -#define DEF_PA 0xff - -struct kcal_lut_data { - int red; - int green; - int blue; - int minimum; - int enable; - int invert; - int sat; - int hue; - int val; - int cont; -}; - -void mdss_mdp_pp_kcal_enable(bool enable); -void mdss_mdp_pp_kcal_update(int kr, int kg, int kb); -void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data); -void mdss_mdp_pp_kcal_invert(int enable); -#endif diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index ebad99e109f07..f6453a724a3d3 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -23,8 +23,6 @@ #include #include -#include "mdss_mdp_kcal_ctrl.h" - struct mdp_csc_cfg mdp_csc_convert[MDSS_MDP_MAX_CSC] = { [MDSS_MDP_CSC_YUV2RGB_601L] = { 0, @@ -400,33 +398,6 @@ struct mdss_pp_res_type { struct mdp_pcc_cfg_data user_pcc_disp_cfg[MDSS_BLOCK_DISP_NUM]; }; -uint32_t igc_Table_RGB[256] = { - 4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, - 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, - 3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, - 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, - 3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, - 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, - 2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, - 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, - 2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, - 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, - 2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, - 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, - 1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, - 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, - 1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, - 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, - 1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, - 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, - 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, - 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, - 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, - 48, 32, 16, 0 -}; -int igc_c0_c1[256] = {0,}; -int igc_c2[256] = {0,}; - static DEFINE_MUTEX(mdss_pp_mutex); static struct mdss_pp_res_type *mdss_pp_res; @@ -520,7 +491,6 @@ static int pp_ad_linearize_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out, static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, bool *bl_out_notify); static int pp_ad_shutdown_cleanup(struct msm_fb_data_type *mfd); -static struct msm_fb_data_type *mdss_get_mfd_from_index(int index); static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num); static inline bool pp_sts_is_enabled(u32 sts, int side); static inline void pp_sts_set_split_bits(u32 *sts, u32 bits); @@ -2112,10 +2082,6 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) mdss_pp_res->gamut_disp_cfg[disp_num].flags |= MDP_PP_OPS_WRITE; } - - if (!disp_num) - pp_sts.pgc_sts |= PP_STS_ENABLE; - if (pp_sts.pgc_sts & PP_STS_ENABLE) { flags |= PP_FLAGS_DIRTY_PGC; if (!(mdss_pp_res->pgc_disp_cfg[disp_num].flags @@ -2167,93 +2133,6 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) return 0; } -void mdss_mdp_pp_kcal_enable(bool enable) -{ - u32 disp_num = 0; - struct mdp_pgc_lut_data *pgc_config; - - pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; - pgc_config->block = MDP_LOGICAL_BLOCK_DISP_0; - - if (enable) { - pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pgc_config->r_data = &mdss_pp_res->gc_lut_r[disp_num][0]; - pgc_config->g_data = &mdss_pp_res->gc_lut_g[disp_num][0]; - pgc_config->b_data = &mdss_pp_res->gc_lut_b[disp_num][0]; - } else - pgc_config->flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; - - mdss_pp_res->pgc_disp_cfg[disp_num] = *pgc_config; - mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PGC; -} - -void mdss_mdp_pp_kcal_update(int kr, int kg, int kb) -{ - int i; - u32 disp_num = 0; - struct mdp_pgc_lut_data *pgc_config; - - pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; - - for (i = 0; i < GC_LUT_SEGMENTS; i++) { - pgc_config->r_data[i].slope = kr; - pgc_config->g_data[i].slope = kg; - pgc_config->b_data[i].slope = kb; - } - - mdss_pp_res->pgc_disp_cfg[disp_num] = *pgc_config; - mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PGC; -} - -void mdss_mdp_pp_kcal_pa(struct kcal_lut_data *lut_data) -{ - u32 copyback = 0; - struct mdp_pa_cfg_data pa_config; - - memset(&pa_config, 0, sizeof(struct mdp_pa_cfg_data)); - - pa_config.block = MDP_LOGICAL_BLOCK_DISP_0; - pa_config.pa_data.flags = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - pa_config.pa_data.sat_adj = lut_data->sat; - pa_config.pa_data.hue_adj = lut_data->hue; - pa_config.pa_data.val_adj = lut_data->val; - pa_config.pa_data.cont_adj = lut_data->cont; - - mdss_mdp_pa_config(&pa_config, ©back); -} - -void mdss_mdp_pp_kcal_invert(int enable) -{ - int i; - u32 disp_num = 0, copyback = 0, copy_from_kernel = 1; - struct msm_fb_data_type *igc_mfd; - struct mdp_igc_lut_data *igc_config; - - igc_mfd = mdss_get_mfd_from_index(0); - - igc_config = &mdss_pp_res->igc_disp_cfg[disp_num]; - igc_config->c0_c1_data = &mdss_pp_res->igc_lut_c0c1[disp_num][0]; - igc_config->c2_data = &mdss_pp_res->igc_lut_c2[disp_num][0]; - igc_config->block = MDP_LOGICAL_BLOCK_DISP_0; - igc_config->len = IGC_LUT_ENTRIES; - - if (igc_mfd && enable) { - igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; - for (i = 0; i < IGC_LUT_ENTRIES; i++) { - igc_c0_c1[i] = (igc_Table_RGB[i] & 0xfff) | - ((igc_Table_RGB[i] & 0xfff)) << 16; - igc_c2[i] = igc_Table_RGB[i]; - } - igc_config->c0_c1_data = &igc_c0_c1[0]; - igc_config->c2_data = &igc_c2[0]; - } else if (igc_mfd && !enable) - igc_config->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; - else - return; - - mdss_mdp_igc_lut_config(igc_config, ©back, copy_from_kernel); -} - int mdss_mdp_pp_init(struct device *dev) { int i, ret = 0; From 086d1d88c17e1b4643c152a07bd3b65b689aeeac Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:52:36 +0100 Subject: [PATCH 126/296] Revert "mdss_fb: add backlight dimmer option" This reverts commit d85c56feb95404b2f68208d073d68b4cfbb24fa5. Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_fb.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index c223797e05fe3..145705e0250ba 100755 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -72,12 +72,6 @@ #define BLANK_FLAG_LP FB_BLANK_VSYNC_SUSPEND #define BLANK_FLAG_ULP FB_BLANK_NORMAL -#define MDSS_BRIGHT_TO_BL_DIM(out, v) do {\ - out = (12*v*v+1393*v+3060)/4465;\ - } while (0) -bool backlight_dimmer = false; -module_param(backlight_dimmer, bool, 0755); - static struct fb_info *fbi_list[MAX_FBI_LIST]; static int fbi_list_index; @@ -276,15 +270,10 @@ static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev, if(!value) count = 1; #endif - - if (backlight_dimmer) { - MDSS_BRIGHT_TO_BL_DIM(bl_lvl, value); - } else { - /* This maps android backlight level 0 to 255 into - driver backlight level 0 to bl_max with rounding */ - MDSS_BRIGHT_TO_BL(bl_lvl, value, mfd->panel_info->bl_max, - mfd->panel_info->brightness_max); - } + /* This maps android backlight level 0 to 255 into + driver backlight level 0 to bl_max with rounding */ + MDSS_BRIGHT_TO_BL(bl_lvl, value, mfd->panel_info->bl_max, + mfd->panel_info->brightness_max); if (!bl_lvl && value) bl_lvl = 1; From 1b0d9e818d2d422282def80c88af9fa152013213 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sat, 16 Jun 2018 18:53:47 +0100 Subject: [PATCH 127/296] haruhi_defconfig: remove junk values Signed-off-by: anupritaisno1 --- arch/arm64/configs/haruhi_defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/configs/haruhi_defconfig b/arch/arm64/configs/haruhi_defconfig index f4bc375aea3ae..c5f016365ddfe 100644 --- a/arch/arm64/configs/haruhi_defconfig +++ b/arch/arm64/configs/haruhi_defconfig @@ -598,7 +598,6 @@ CONFIG_DEFAULT_WESTWOOD=y CONFIG_DEFAULT_TCP_CONG="westwood" CONFIG_WIREGUARD=y # CONFIG_WIREGUARD_DEBUG is not set -CONFIG_FB_MSM_MDSS_KCAL_CTRL=y CONFIG_CRYPTO_SHA256_ARM64=y CONFIG_CRYPTO_SHA512_ARM64=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y From c0c1c946bf406a5800b4834edd9bfc591d9541c9 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Mon, 18 Jun 2018 18:24:24 +0100 Subject: [PATCH 128/296] fs: import sdFAT driver Op2 has a license to use exFAT on oxygenos Change-Id: Ia4c44765f1864e7ea4544a09f26628d1aec27a02 Signed-off-by: anupritaisno1 --- arch/arm64/configs/haruhi_defconfig | 11 + fs/Kconfig | 1 + fs/Makefile | 1 + fs/sdfat/Kconfig | 122 + fs/sdfat/LICENSE | 339 ++ fs/sdfat/Makefile | 24 + fs/sdfat/README.md | 19 + fs/sdfat/amap_smart.c | 1314 +++++++ fs/sdfat/amap_smart.h | 137 + fs/sdfat/api.c | 629 ++++ fs/sdfat/api.h | 397 +++ fs/sdfat/blkdev.c | 416 +++ fs/sdfat/cache.c | 842 +++++ fs/sdfat/config.h | 146 + fs/sdfat/core.c | 3666 +++++++++++++++++++ fs/sdfat/core.h | 218 ++ fs/sdfat/core_exfat.c | 1554 ++++++++ fs/sdfat/core_fat.c | 1463 ++++++++ fs/sdfat/dfr.c | 1372 +++++++ fs/sdfat/dfr.h | 261 ++ fs/sdfat/extent.c | 351 ++ fs/sdfat/fatent.c | 420 +++ fs/sdfat/misc.c | 386 ++ fs/sdfat/mpage.c | 622 ++++ fs/sdfat/nls.c | 478 +++ fs/sdfat/sdfat.c | 5133 +++++++++++++++++++++++++++ fs/sdfat/sdfat.h | 508 +++ fs/sdfat/sdfat_fs.h | 416 +++ fs/sdfat/statistics.c | 262 ++ fs/sdfat/upcase.h | 407 +++ fs/sdfat/version.h | 25 + fs/sdfat/xattr.c | 132 + 32 files changed, 22072 insertions(+) create mode 100644 fs/sdfat/Kconfig create mode 100644 fs/sdfat/LICENSE create mode 100644 fs/sdfat/Makefile create mode 100644 fs/sdfat/README.md create mode 100644 fs/sdfat/amap_smart.c create mode 100644 fs/sdfat/amap_smart.h create mode 100644 fs/sdfat/api.c create mode 100644 fs/sdfat/api.h create mode 100644 fs/sdfat/blkdev.c create mode 100644 fs/sdfat/cache.c create mode 100644 fs/sdfat/config.h create mode 100644 fs/sdfat/core.c create mode 100644 fs/sdfat/core.h create mode 100644 fs/sdfat/core_exfat.c create mode 100644 fs/sdfat/core_fat.c create mode 100644 fs/sdfat/dfr.c create mode 100644 fs/sdfat/dfr.h create mode 100644 fs/sdfat/extent.c create mode 100644 fs/sdfat/fatent.c create mode 100644 fs/sdfat/misc.c create mode 100644 fs/sdfat/mpage.c create mode 100644 fs/sdfat/nls.c create mode 100644 fs/sdfat/sdfat.c create mode 100644 fs/sdfat/sdfat.h create mode 100644 fs/sdfat/sdfat_fs.h create mode 100644 fs/sdfat/statistics.c create mode 100644 fs/sdfat/upcase.h create mode 100644 fs/sdfat/version.h create mode 100644 fs/sdfat/xattr.c diff --git a/arch/arm64/configs/haruhi_defconfig b/arch/arm64/configs/haruhi_defconfig index c5f016365ddfe..f8127a95cb7f7 100644 --- a/arch/arm64/configs/haruhi_defconfig +++ b/arch/arm64/configs/haruhi_defconfig @@ -606,3 +606,14 @@ CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y # CONFIG_TCP_CONG_CUBIC is not set # CONFIG_TCP_CONG_HTCP is not set CONFIG_EXFAT_FS=y +CONFIG_SDFAT_FS=y +CONFIG_SDFAT_DELAYED_META_DIRTY=y +CONFIG_SDFAT_SUPPORT_DIR_SYNC=y +CONFIG_SDFAT_DEFAULT_CODEPAGE=437 +CONFIG_SDFAT_DEFAULT_IOCHARSET="utf8" +CONFIG_SDFAT_ALIGNED_MPAGE_WRITE=y +CONFIG_SDFAT_VIRTUAL_XATTR=y +CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL="u:object_r:vfat:s0" +CONFIG_SDFAT_DEBUG=y +CONFIG_SDFAT_DBG_MSG=y +CONFIG_SDFAT_STATISTICS=y diff --git a/fs/Kconfig b/fs/Kconfig index 2618766145c45..a8a88c1d07055 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -96,6 +96,7 @@ menu "DOS/FAT/NT Filesystems" source "fs/fat/Kconfig" source "fs/ntfs/Kconfig" +source "fs/sdfat/Kconfig" #ifdef VENDOR_EDIT source "fs/exfat/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index ce574e3f2dd91..01774bdc8eaf6 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ obj-$(CONFIG_CODA_FS) += coda/ obj-$(CONFIG_MINIX_FS) += minix/ obj-$(CONFIG_FAT_FS) += fat/ +obj-$(CONFIG_SDFAT_FS) += sdfat/ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ diff --git a/fs/sdfat/Kconfig b/fs/sdfat/Kconfig new file mode 100644 index 0000000000000..3a15bbb55526f --- /dev/null +++ b/fs/sdfat/Kconfig @@ -0,0 +1,122 @@ +config SDFAT_FS + tristate "sdFAT filesystem support" + default y + select NLS + select NLS_UTF8 + select NLS_CODEPAGE_437 + select NLS_ISO8859_1 + help + If you want to use the sdFAT file system, then you must say Y or M + here to inlucde sdFAT support. + sdFAT is unified FAT-based file system which supports not only fat12/ + 16/32 with vfat but also exfat. sdFAT supports winnt short-name rule. + (winnt: emulate the Windows NT rule for display/create.) + + To compile this as a module, choose M here: the module will be called + sdfat_core and sdfat_fs. + +config SDFAT_USE_FOR_EXFAT + bool "Register sdFAT as exFAT" + default y + depends on SDFAT_FS && !EXFAT_FS + help + If you want to register sdFAT as available for exFAT, say Y. + +config SDFAT_USE_FOR_VFAT + bool "Register sdFAT as VFAT" + default y + depends on SDFAT_FS && !VFAT_FS + help + If you want to register sdFAT as available for VFAT, say Y. + +config SDFAT_DELAYED_META_DIRTY + bool "Enable delayed metadata dirty" + default y + depends on SDFAT_FS + help + If you enable this feature, metadata(FAT/Directory entry) is updated + by flush thread. + +config SDFAT_SUPPORT_DIR_SYNC + bool "Enable supporting dir sync" + default n + depends on SDFAT_FS + help + If you enable this feature, the modification for directory operation + is written to a storage at once. + +config SDFAT_DEFAULT_CODEPAGE + int "Default codepage for sdFAT" + default 437 + depends on SDFAT_FS + help + This option should be set to the codepage of your sdFAT filesystems. + +config SDFAT_DEFAULT_IOCHARSET + string "Default iocharset for sdFAT" + default "utf8" + depends on SDFAT_FS + help + Set this to the default input/output character set you'd + like sdFAT to use. It should probably match the character set + that most of your sdFAT filesystems use, and can be overridden + with the "iocharset" mount option for sdFAT filesystems. + +config SDFAT_CHECK_RO_ATTR + bool "Check read-only attribute" + default n + depends on SDFAT_FS + +config SDFAT_ALIGNED_MPAGE_WRITE + bool "Enable supporting aligned mpage_write" + default y + depends on SDFAT_FS + +config SDFAT_VIRTUAL_XATTR + bool "Virtual xattr support for sdFAT" + default y + depends on SDFAT_FS + help + To support virtual xattr. + +config SDFAT_VIRTUAL_XATTR_SELINUX_LABEL + string "Default string for SELinux label" + default "u:object_r:sdcard_external:s0" + depends on SDFAT_FS && SDFAT_VIRTUAL_XATTR + help + Set this to the default string for SELinux label. + +config SDFAT_SUPPORT_STLOG + bool "Enable storage log" + default y + depends on SDFAT_FS && PROC_STLOG + +config SDFAT_DEBUG + bool "enable debug features" + depends on SDFAT_FS + default y + +config SDFAT_DBG_IOCTL + bool "enable debug-ioctl features" + depends on SDFAT_FS && SDFAT_DEBUG + default n + +config SDFAT_DBG_MSG + bool "enable debug messages" + depends on SDFAT_FS && SDFAT_DEBUG + default y + +config SDFAT_DBG_BUGON + bool "enable strict BUG_ON() for debugging" + depends on SDFAT_FS && SDFAT_DEBUG + default n + +config SDFAT_DBG_WARNON + bool "enable strict WARN_ON() for debugging" + depends on SDFAT_FS && SDFAT_DEBUG + default n + +config SDFAT_STATISTICS + bool "enable statistics for bigdata" + depends on SDFAT_FS + default y diff --git a/fs/sdfat/LICENSE b/fs/sdfat/LICENSE new file mode 100644 index 0000000000000..d159169d10508 --- /dev/null +++ b/fs/sdfat/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/fs/sdfat/Makefile b/fs/sdfat/Makefile new file mode 100644 index 0000000000000..a5cd0858c6caa --- /dev/null +++ b/fs/sdfat/Makefile @@ -0,0 +1,24 @@ +# +# Makefile for the linux FAT12/16/32(VFAT)/64(exFAT) filesystem driver. +# + +obj-$(CONFIG_SDFAT_FS) += sdfat_fs.o + +sdfat_fs-objs := sdfat.o core.o core_fat.o core_exfat.o api.o blkdev.o \ + fatent.o amap_smart.o cache.o dfr.o nls.o misc.o \ + mpage.o extent.o + +sdfat_fs-$(CONFIG_SDFAT_VIRTUAL_XATTR) += xattr.o +sdfat_fs-$(CONFIG_SDFAT_STATISTICS) += statistics.o + + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + +cscope: + rm -rf cscope.files cscope.files + find $(PWD) \( -name '*.c' -o -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.s' -o -name '*.S' \) -print > cscope.files + cscope diff --git a/fs/sdfat/README.md b/fs/sdfat/README.md new file mode 100644 index 0000000000000..f291e8f1ae00b --- /dev/null +++ b/fs/sdfat/README.md @@ -0,0 +1,19 @@ +sdFAT FS support for Linux Kernel 4.4 +===================================== + +sdFAT is unified FAT-based file system which supports not only fat12/16/32 with +vfat but also exfat. sdFAT supports winnt short-name rule. + +Suggested Kernel config: + + CONFIG_SDFAT_FS=y + CONFIG_SDFAT_DELAYED_META_DIRTY=y + CONFIG_SDFAT_SUPPORT_DIR_SYNC=y + CONFIG_SDFAT_DEFAULT_CODEPAGE=437 + CONFIG_SDFAT_DEFAULT_IOCHARSET="utf8" + CONFIG_SDFAT_ALIGNED_MPAGE_WRITE=y + CONFIG_SDFAT_VIRTUAL_XATTR=y + CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL="u:object_r:vfat:s0" + CONFIG_SDFAT_DEBUG=y + CONFIG_SDFAT_DBG_MSG=y + CONFIG_SDFAT_STATISTICS=y diff --git a/fs/sdfat/amap_smart.c b/fs/sdfat/amap_smart.c new file mode 100644 index 0000000000000..b556f868d76e6 --- /dev/null +++ b/fs/sdfat/amap_smart.c @@ -0,0 +1,1314 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : amap_smart.c */ +/* PURPOSE : FAT32 Smart allocation code for sdFAT */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include + +#include "sdfat.h" +#include "core.h" +#include "amap_smart.h" + +/* AU list related functions */ +static inline void amap_list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + + /* Will be used to check if the entry is a single entry(selected) */ + entry->prev = NULL; + entry->next = NULL; +} + +static inline int amap_insert_to_list(AU_INFO_T *au, struct slist_head *shead) +{ + struct slist_head *entry = &au->shead; + + ASSERT(!entry->head); + + entry->next = shead->next; + entry->head = shead; + + shead->next = entry; + + return 0; +} + +static inline int amap_remove_from_list(AU_INFO_T *au, struct slist_head *shead) +{ + struct slist_head *entry = &au->shead; + struct slist_head *iter; + + BUG_ON(entry->head != shead); + + iter = shead; + + while (iter->next) { + if (iter->next == entry) { + // iter->next = iter->next->next + iter->next = entry->next; + + entry->next = NULL; + entry->head = NULL; + return 0; + } + iter = iter->next; + } + + BUG_ON("Not reachable"); +} + +/* Full-linear serach => Find AU with max. number of fclu */ +static inline AU_INFO_T *amap_find_hot_au_largest(struct slist_head *shead) +{ + struct slist_head *iter; + uint16_t max_fclu = 0; + AU_INFO_T *entry, *ret = NULL; + + ASSERT(shead->head == shead); /* Singly-list condition */ + ASSERT(shead->next != shead); + + iter = shead->next; + + while (iter) { + entry = list_entry(iter, AU_INFO_T, shead); + + if (entry->free_clusters > max_fclu) { + max_fclu = entry->free_clusters; + ret = entry; + } + + iter = iter->next; + } + + return ret; +} + +/* Find partially used AU with max. number of fclu. + * If there is no partial AU available, pick a clean one + */ +static inline AU_INFO_T *amap_find_hot_au_partial(AMAP_T *amap) +{ + struct slist_head *iter; + uint16_t max_fclu = 0; + AU_INFO_T *entry, *ret = NULL; + + iter = &amap->slist_hot; + ASSERT(iter->head == iter); /* Singly-list condition */ + ASSERT(iter->next != iter); + + iter = iter->next; + + while (iter) { + entry = list_entry(iter, AU_INFO_T, shead); + + if (entry->free_clusters > max_fclu) { + if (entry->free_clusters < amap->clusters_per_au) { + max_fclu = entry->free_clusters; + ret = entry; + } else { + if (!ret) + ret = entry; + } + } + + iter = iter->next; + } + + return ret; +} + + + + +/* + * Size-base AU management functions + */ + +/* + * Add au into cold AU MAP + * au: an isolated (not in a list) AU data structure + */ +int amap_add_cold_au(AMAP_T *amap, AU_INFO_T *au) +{ + FCLU_NODE_T *fclu_node = NULL; + + /* Check if a single entry */ + BUG_ON(au->head.prev); + + /* Ignore if the au is full */ + if (!au->free_clusters) + return 0; + + /* Find entry */ + fclu_node = NODE(au->free_clusters, amap); + + /* Insert to the list */ + list_add_tail(&(au->head), &(fclu_node->head)); + + /* Update fclu_hint (Increase) */ + if (au->free_clusters > amap->fclu_hint) + amap->fclu_hint = au->free_clusters; + + return 0; +} + +/* + * Remove an AU from AU MAP + */ +int amap_remove_cold_au(AMAP_T *amap, AU_INFO_T *au) +{ + struct list_head *prev = au->head.prev; + + /* Single entries are not managed in lists */ + if (!prev) { + BUG_ON(au->free_clusters > 0); + return 0; + } + + /* remove from list */ + amap_list_del(&(au->head)); + + return 0; +} + + +/* "Find" best fit AU + * returns NULL if there is no AU w/ enough free space. + * + * This function doesn't change AU status. + * The caller should call amap_remove_cold_au() if needed. + */ +AU_INFO_T *amap_find_cold_au_bestfit(AMAP_T *amap, uint16_t free_clusters) +{ + AU_INFO_T *au = NULL; + FCLU_NODE_T *fclu_iter; + + if (free_clusters <= 0 || free_clusters > amap->clusters_per_au) { + EMSG("AMAP: amap_find_cold_au_bestfit / unexpected arg. (%d)\n", + free_clusters); + return NULL; + } + + fclu_iter = NODE(free_clusters, amap); + + if (amap->fclu_hint < free_clusters) { + /* There is no AUs with enough free_clusters */ + return NULL; + } + + /* Naive Hash management (++) */ + do { + if (!list_empty(&fclu_iter->head)) { + struct list_head *first = fclu_iter->head.next; + + au = list_entry(first, AU_INFO_T, head); + + break; + } + + fclu_iter++; + } while (fclu_iter < (amap->fclu_nodes + amap->clusters_per_au)); + + + // BUG_ON(au->free_clusters < 0); + BUG_ON(au && (au->free_clusters > amap->clusters_per_au)); + + return au; +} + + +/* "Pop" best fit AU + * + * returns NULL if there is no AU w/ enough free space. + * The returned AU will not be in the list anymore. + */ +AU_INFO_T *amap_pop_cold_au_bestfit(AMAP_T *amap, uint16_t free_clusters) +{ + /* Naive implementation */ + AU_INFO_T *au; + + au = amap_find_cold_au_bestfit(amap, free_clusters); + if (au) + amap_remove_cold_au(amap, au); + + return au; +} + + + +/* Pop the AU with the largest free space + * + * search from 'start_fclu' to 0 + * (target freecluster : -1 for each step) + * start_fclu = 0 means to search from the max. value + */ +AU_INFO_T *amap_pop_cold_au_largest(AMAP_T *amap, uint16_t start_fclu) +{ + AU_INFO_T *au = NULL; + FCLU_NODE_T *fclu_iter; + + if (!start_fclu) + start_fclu = amap->clusters_per_au; + if (start_fclu > amap->clusters_per_au) + start_fclu = amap->clusters_per_au; + + /* Use hint (search start point) */ + if (amap->fclu_hint < start_fclu) + fclu_iter = NODE(amap->fclu_hint, amap); + else + fclu_iter = NODE(start_fclu, amap); + + /* Naive Hash management */ + do { + if (!list_empty(&fclu_iter->head)) { + struct list_head *first = fclu_iter->head.next; + + au = list_entry(first, AU_INFO_T, head); + // BUG_ON((au < amap->entries) || ((amap->entries + amap->n_au) <= au)); + + amap_list_del(first); + + // (Hint) Possible maximum value of free clusters (among cold) + /* if it wasn't the whole search, don't update fclu_hint */ + if (start_fclu == amap->clusters_per_au) + amap->fclu_hint = au->free_clusters; + + break; + } + + fclu_iter--; + } while (amap->fclu_nodes <= fclu_iter); + + return au; +} + + + +/* + * =============================================== + * Allocation Map related functions + * =============================================== + */ + +/* Create AMAP related data structure (mount time) */ +int amap_create(struct super_block *sb, u32 pack_ratio, u32 sect_per_au, u32 hidden_sect) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + AMAP_T *amap; + int total_used_clusters; + int n_au_table = 0; + int i, i_clu, i_au; + int i_au_root = -1, i_au_hot_from = INT_MAX; + u32 misaligned_sect = hidden_sect; + u64 tmp; + + BUG_ON(!fsi->bd_opened); + + if (fsi->amap) + return -EEXIST; + + /* Check conditions */ + if (fsi->vol_type != FAT32) { + sdfat_msg(sb, KERN_ERR, "smart allocation is only available " + "with fat32-fs"); + return -ENOTSUPP; + } + + if (fsi->num_sectors < AMAP_MIN_SUPPORT_SECTORS) { + sdfat_msg(sb, KERN_ERR, "smart allocation is only available " + "with sectors above %d", AMAP_MIN_SUPPORT_SECTORS); + return -ENOTSUPP; + } + + /* AU size must be a multiple of clu_size */ + if ((sect_per_au <= 0) || (sect_per_au & (fsi->sect_per_clus - 1))) { + sdfat_msg(sb, KERN_ERR, + "invalid AU size (sect_per_au : %u, " + "sect_per_clus : %u) " + "please re-format for performance.", + sect_per_au, fsi->sect_per_clus); + return -EINVAL; + } + + /* the start sector of this partition must be a multiple of clu_size */ + if (misaligned_sect & (fsi->sect_per_clus - 1)) { + sdfat_msg(sb, KERN_ERR, + "misaligned part (start sect : %u, " + "sect_per_clus : %u) " + "please re-format for performance.", + misaligned_sect, fsi->sect_per_clus); + return -EINVAL; + } + + /* data start sector must be a multiple of clu_size */ + if (fsi->data_start_sector & (fsi->sect_per_clus - 1)) { + sdfat_msg(sb, KERN_ERR, + "misaligned data area (start sect : %llu, " + "sect_per_clus : %u) " + "please re-format for performance.", + fsi->data_start_sector, fsi->sect_per_clus); + return -EINVAL; + } + + misaligned_sect &= (sect_per_au - 1); + + /* Allocate data structrues */ + amap = kzalloc(sizeof(AMAP_T), GFP_NOIO); + if (!amap) + return -ENOMEM; + + amap->sb = sb; + + tmp = fsi->num_sectors + misaligned_sect + sect_per_au - 1; + do_div(tmp, sect_per_au); + amap->n_au = tmp; + amap->n_clean_au = 0; + amap->n_full_au = 0; + + /* Reflect block-partition align first, + * then partition-data_start align + */ + amap->clu_align_bias = (misaligned_sect / fsi->sect_per_clus); + amap->clu_align_bias += (fsi->data_start_sector >> fsi->sect_per_clus_bits) - CLUS_BASE; + amap->clusters_per_au = sect_per_au / fsi->sect_per_clus; + + /* That is, + * the size of cluster is at least 4KB if the size of AU is 4MB + */ + if (amap->clusters_per_au > MAX_CLU_PER_AU) { + sdfat_log_msg(sb, KERN_INFO, + "too many clusters per AU (clus/au:%d > %d).", + amap->clusters_per_au, + MAX_CLU_PER_AU); + } + + /* is it needed? why here? */ + // set_sb_dirty(sb); + + spin_lock_init(&amap->amap_lock); + + amap->option.packing_ratio = pack_ratio; + amap->option.au_size = sect_per_au; + amap->option.au_align_factor = hidden_sect; + + + /* Allocate AU info table */ + n_au_table = (amap->n_au + N_AU_PER_TABLE - 1) / N_AU_PER_TABLE; + amap->au_table = kmalloc(sizeof(AU_INFO_T *) * n_au_table, GFP_NOIO); + if (!amap->au_table) { + sdfat_msg(sb, KERN_ERR, + "failed to alloc amap->au_table\n"); + kfree(amap); + return -ENOMEM; + } + + for (i = 0; i < n_au_table; i++) + amap->au_table[i] = (AU_INFO_T *)get_zeroed_page(GFP_NOIO); + + /* Allocate buckets indexed by # of free clusters */ + amap->fclu_order = get_order(sizeof(FCLU_NODE_T) * amap->clusters_per_au); + + // XXX: amap->clusters_per_au limitation is 512 (w/ 8 byte list_head) + sdfat_log_msg(sb, KERN_INFO, "page orders for AU nodes : %d " + "(clus_per_au : %d, node_size : %lu)", + amap->fclu_order, + amap->clusters_per_au, + (unsigned long)sizeof(FCLU_NODE_T)); + + if (!amap->fclu_order) + amap->fclu_nodes = (FCLU_NODE_T *)get_zeroed_page(GFP_NOIO); + else + amap->fclu_nodes = vzalloc(PAGE_SIZE << amap->fclu_order); + + amap->fclu_hint = amap->clusters_per_au; + + /* Hot AU list, ignored AU list */ + amap->slist_hot.next = NULL; + amap->slist_hot.head = &amap->slist_hot; + amap->total_fclu_hot = 0; + + amap->slist_ignored.next = NULL; + amap->slist_ignored.head = &amap->slist_ignored; + + /* Strategy related vars. */ + amap->cur_cold.au = NULL; + amap->cur_hot.au = NULL; + amap->n_need_packing = 0; + + + /* Build AMAP info */ + total_used_clusters = 0; // Count # of used clusters + + i_au_root = i_AU_of_CLU(amap, fsi->root_dir); + i_au_hot_from = amap->n_au - (SMART_ALLOC_N_HOT_AU - 1); + + for (i = 0; i < amap->clusters_per_au; i++) + INIT_LIST_HEAD(&amap->fclu_nodes[i].head); + + /* + * Thanks to kzalloc() + * amap->entries[i_au].free_clusters = 0; + * amap->entries[i_au].head.prev = NULL; + * amap->entries[i_au].head.next = NULL; + */ + + /* Parse FAT table */ + for (i_clu = CLUS_BASE; i_clu < fsi->num_clusters; i_clu++) { + u32 clu_data; + AU_INFO_T *au; + + if (fat_ent_get(sb, i_clu, &clu_data)) { + sdfat_msg(sb, KERN_ERR, + "failed to read fat entry(%u)\n", i_clu); + goto free_and_eio; + } + + if (IS_CLUS_FREE(clu_data)) { + au = GET_AU(amap, i_AU_of_CLU(amap, i_clu)); + au->free_clusters++; + } else + total_used_clusters++; + } + + /* Build AU list */ + for (i_au = 0; i_au < amap->n_au; i_au++) { + AU_INFO_T *au = GET_AU(amap, i_au); + + au->idx = i_au; + BUG_ON(au->free_clusters > amap->clusters_per_au); + + if (au->free_clusters == amap->clusters_per_au) + amap->n_clean_au++; + else if (au->free_clusters == 0) + amap->n_full_au++; + + /* If hot, insert to the hot list */ + if (i_au >= i_au_hot_from) { + amap_add_hot_au(amap, au); + amap->total_fclu_hot += au->free_clusters; + } else if (i_au != i_au_root || SMART_ALLOC_N_HOT_AU == 0) { + /* Otherwise, insert to the free cluster hash */ + amap_add_cold_au(amap, au); + } + } + + /* Hot list -> (root) -> (last) -> (last - 1) -> ... */ + if (i_au_root >= 0 && SMART_ALLOC_N_HOT_AU > 0) { + amap_add_hot_au(amap, GET_AU(amap, i_au_root)); + amap->total_fclu_hot += GET_AU(amap, i_au_root)->free_clusters; + } + + fsi->amap = amap; + fsi->used_clusters = total_used_clusters; + + sdfat_msg(sb, KERN_INFO, + "AMAP: Smart allocation enabled (opt : %u / %u / %u)", + amap->option.au_size, amap->option.au_align_factor, + amap->option.packing_ratio); + + /* Debug purpose - check */ + //{ + //u32 used_clusters; + //fat_count_used_clusters(sb, &used_clusters) + //ASSERT(used_clusters == total_used_clusters); + //} + + return 0; + + +free_and_eio: + if (amap) { + if (amap->au_table) { + for (i = 0; i < n_au_table; i++) + free_page((unsigned long)amap->au_table[i]); + kfree(amap->au_table); + } + if (amap->fclu_nodes) { + if (!amap->fclu_order) + free_page((unsigned long)amap->fclu_nodes); + else + vfree(amap->fclu_nodes); + } + kfree(amap); + } + return -EIO; +} + + +/* Free AMAP related structure */ +void amap_destroy(struct super_block *sb) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + int n_au_table; + + if (!amap) + return; + + DMSG("%s\n", __func__); + + n_au_table = (amap->n_au + N_AU_PER_TABLE - 1) / N_AU_PER_TABLE; + + if (amap->au_table) { + int i; + + for (i = 0; i < n_au_table; i++) + free_page((unsigned long)amap->au_table[i]); + + kfree(amap->au_table); + } + if (!amap->fclu_order) + free_page((unsigned long)amap->fclu_nodes); + else + vfree(amap->fclu_nodes); + kfree(amap); + SDFAT_SB(sb)->fsi.amap = NULL; +} + + +/* + * Check status of FS + * and change destination if needed to disable AU-aligned alloc. + * (from ALLOC_COLD_ALIGNED to ALLOC_COLD_SEQ) + */ +static inline int amap_update_dest(AMAP_T *amap, int ori_dest) +{ + FS_INFO_T *fsi = &(SDFAT_SB(amap->sb)->fsi); + int n_partial_au, n_partial_freeclus; + + if (ori_dest != ALLOC_COLD_ALIGNED) + return ori_dest; + + /* # of partial AUs and # of clusters in those AUs */ + n_partial_au = amap->n_au - amap->n_clean_au - amap->n_full_au; + n_partial_freeclus = fsi->num_clusters - fsi->used_clusters - + amap->clusters_per_au * amap->n_clean_au; + + /* Status of AUs : Full / Partial / Clean + * If there are many partial (and badly fragmented) AUs, + * the throughput will decrease extremly. + * + * The follow code will treat those worst cases. + */ + + /* XXX: AMAP heuristics */ + if ((amap->n_clean_au * 50 <= amap->n_au) && + (n_partial_freeclus*2) < (n_partial_au*amap->clusters_per_au)) { + /* If clean AUs are fewer than 2% of n_au (80 AUs per 16GB) + * and fragment ratio is more than 2 (AVG free_clusters=half AU) + * + * disable clean-first allocation + * enable VFAT-like sequential allocation + */ + return ALLOC_COLD_SEQ; + } + + return ori_dest; +} + + +#define PACKING_SOFTLIMIT (amap->option.packing_ratio) +#define PACKING_HARDLIMIT (amap->option.packing_ratio * 4) +/* + * Pick a packing AU if needed. + * Otherwise just return NULL + * + * This function includes some heuristics. + */ +static inline AU_INFO_T *amap_get_packing_au(AMAP_T *amap, int dest, int num_to_wb, int *clu_to_skip) +{ + AU_INFO_T *au = NULL; + + if (dest == ALLOC_COLD_PACKING) { + /* ALLOC_COLD_PACKING: + * Packing-first mode for defrag. + * Optimized to save clean AU + * + * 1) best-fit AU + * 2) Smallest AU (w/ minimum free clusters) + */ + if (num_to_wb >= amap->clusters_per_au) + num_to_wb = num_to_wb % amap->clusters_per_au; + + /* 이거 주석처리하면, AU size 딱 맞을때는 clean, 나머지는 작은거부터 */ + if (num_to_wb == 0) + num_to_wb = 1; // Don't use clean AUs + + au = amap_find_cold_au_bestfit(amap, num_to_wb); + if (au && au->free_clusters == amap->clusters_per_au && num_to_wb > 1) { + /* if au is clean then get a new partial one */ + au = amap_find_cold_au_bestfit(amap, 1); + } + + if (au) { + amap->n_need_packing = 0; + amap_remove_cold_au(amap, au); + return au; + } + } + + + /* Heuristic packing: + * This will improve QoS greatly. + * + * Count # of AU_ALIGNED allocation. + * If the number exceeds the specific threshold, + * allocate on a partial AU or generate random I/O. + */ + if ((PACKING_SOFTLIMIT > 0) && + (amap->n_need_packing >= PACKING_SOFTLIMIT) && + (num_to_wb < (int)amap->clusters_per_au)) { + /* Best-fit packing: + * If num_to_wb (expected number to be allocated) is smaller + * than AU_SIZE, find a best-fit AU. + */ + + /* Back margin (heuristics) */ + if (num_to_wb < amap->clusters_per_au / 4) + num_to_wb = amap->clusters_per_au / 4; + + au = amap_find_cold_au_bestfit(amap, num_to_wb); + if (au != NULL) { + amap_remove_cold_au(amap, au); + + MMSG("AMAP: packing (cnt: %d) / softlimit, " + "best-fit (num_to_wb: %d))\n", + amap->n_need_packing, num_to_wb); + + if (au->free_clusters > num_to_wb) { // Best-fit search: if 문 무조건 hit + *clu_to_skip = au->free_clusters - num_to_wb; + /* otherwise don't skip */ + } + amap->n_need_packing = 0; + return au; + } + } + + if ((PACKING_HARDLIMIT) && amap->n_need_packing >= PACKING_HARDLIMIT) { + /* Compulsory SLC flushing: + * If there was no chance to do best-fit packing + * and the # of AU-aligned allocation exceeds HARD threshold, + * then pick a clean AU and generate a compulsory random I/O. + */ + au = amap_pop_cold_au_largest(amap, amap->clusters_per_au); + if (au) { + MMSG("AMAP: packing (cnt: %d) / hard-limit, largest)\n", + amap->n_need_packing); + + if (au->free_clusters >= 96) { + *clu_to_skip = au->free_clusters / 2; + MMSG("AMAP: cluster idx re-position\n"); + } + amap->n_need_packing = 0; + return au; + } + } + + /* Update # of clean AU allocation */ + amap->n_need_packing++; + return NULL; +} + + +/* Pick a target AU: + * This function should be called + * only if there are one or more free clusters in the bdev. + */ +TARGET_AU_T *amap_get_target_au(AMAP_T *amap, int dest, int num_to_wb) +{ + int loop_count = 0; + +retry: + if (++loop_count >= 3) { + /* No space available (or AMAP consistency error) + * This could happen because of the ignored AUs but not likely + * (because the defrag daemon will not work if there is no enough space) + */ + BUG_ON(amap->slist_ignored.next == NULL); + return NULL; + } + + /* Hot clusters (DIR) */ + if (dest == ALLOC_HOT) { + + /* Working hot AU exist? */ + if (amap->cur_hot.au == NULL || amap->cur_hot.au->free_clusters == 0) { + AU_INFO_T *au; + + if (amap->total_fclu_hot == 0) { + /* No more hot AU avaialbe */ + dest = ALLOC_COLD; + + goto retry; + } + + au = amap_find_hot_au_partial(amap); + + BUG_ON(au == NULL); + BUG_ON(au->free_clusters <= 0); + + amap->cur_hot.au = au; + amap->cur_hot.idx = 0; + amap->cur_hot.clu_to_skip = 0; + } + + /* Now allocate on a hot AU */ + return &amap->cur_hot; + } + + /* Cold allocation: + * If amap->cur_cold.au has one or more free cluster(s), + * then just return amap->cur_cold + */ + if ((!amap->cur_cold.au) + || (amap->cur_cold.idx == amap->clusters_per_au) + || (amap->cur_cold.au->free_clusters == 0)) { + + AU_INFO_T *au = NULL; + const AU_INFO_T *old_au = amap->cur_cold.au; + int n_clu_to_skip = 0; + + if (old_au) { + ASSERT(!IS_AU_WORKING(old_au, amap)); + /* must be NOT WORKING AU. + * (only for information gathering) + */ + } + + /* Next target AU is needed: + * There are 3 possible ALLOC options for cold AU + * + * ALLOC_COLD_ALIGNED: Clean AU first, but heuristic packing is ON + * ALLOC_COLD_PACKING: Packing AU first (usually for defrag) + * ALLOC_COLD_SEQ : Sequential AU allocation (VFAT-like) + */ + + /* Experimental: Modify allocation destination if needed (ALIGNED => SEQ) */ + // dest = amap_update_dest(amap, dest); + + if ((dest == ALLOC_COLD_SEQ) && old_au) { + int i_au = old_au->idx + 1; + + while (i_au != old_au->idx) { + au = GET_AU(amap, i_au); + + if ((au->free_clusters > 0) && + !IS_AU_HOT(au, amap) && + !IS_AU_IGNORED(au, amap)) { + MMSG("AMAP: new cold AU(%d) with %d " + "clusters (seq)\n", + au->idx, au->free_clusters); + + amap_remove_cold_au(amap, au); + goto ret_new_cold; + } + i_au++; + if (i_au >= amap->n_au) + i_au = 0; + } + + // no cold AUs are available => Hot allocation + dest = ALLOC_HOT; + goto retry; + } + + + /* + * Check if packing is needed + * (ALLOC_COLD_PACKING is treated by this function) + */ + au = amap_get_packing_au(amap, dest, num_to_wb, &n_clu_to_skip); + if (au) { + MMSG("AMAP: new cold AU(%d) with %d clusters " + "(packing)\n", au->idx, au->free_clusters); + goto ret_new_cold; + } + + /* ALLOC_COLD_ALIGNED */ + /* Check if the adjacent AU is clean */ + if (old_au && ((old_au->idx + 1) < amap->n_au)) { + au = GET_AU(amap, old_au->idx + 1); + if ((au->free_clusters == amap->clusters_per_au) && + !IS_AU_HOT(au, amap) && + !IS_AU_IGNORED(au, amap)) { + MMSG("AMAP: new cold AU(%d) with %d clusters " + "(adjacent)\n", au->idx, au->free_clusters); + amap_remove_cold_au(amap, au); + goto ret_new_cold; + } + } + + /* Clean or largest AU */ + au = amap_pop_cold_au_largest(amap, 0); + if (!au) { + //ASSERT(amap->total_fclu_hot == (fsi->num_clusters - fsi->used_clusters - 2)); + dest = ALLOC_HOT; + goto retry; + } + + MMSG("AMAP: New cold AU (%d) with %d clusters\n", + au->idx, au->free_clusters); + +ret_new_cold: + SET_AU_WORKING(au); + + amap->cur_cold.au = au; + amap->cur_cold.idx = 0; + amap->cur_cold.clu_to_skip = n_clu_to_skip; + } + + return &amap->cur_cold; +} + +/* Put and update target AU */ +void amap_put_target_au(AMAP_T *amap, TARGET_AU_T *cur, unsigned int num_allocated) +{ + /* Update AMAP info vars. */ + if (num_allocated > 0 && + (cur->au->free_clusters + num_allocated) == amap->clusters_per_au) { + /* if the target AU was a clean AU before this allocation ... */ + amap->n_clean_au--; + } + if (num_allocated > 0 && + cur->au->free_clusters == 0) + amap->n_full_au++; + + if (IS_AU_HOT(cur->au, amap)) { + /* Hot AU */ + MMSG("AMAP: hot allocation at AU %d\n", cur->au->idx); + amap->total_fclu_hot -= num_allocated; + + /* Intra-AU round-robin */ + if (cur->idx >= amap->clusters_per_au) + cur->idx = 0; + + /* No more space available */ + if (cur->au->free_clusters == 0) + cur->au = NULL; + + } else { + /* non-hot AU */ + ASSERT(IS_AU_WORKING(cur->au, amap)); + + if (cur->idx >= amap->clusters_per_au || cur->au->free_clusters == 0) { + /* It should be inserted back to AU MAP */ + cur->au->shead.head = NULL; // SET_AU_NOT_WORKING + amap_add_cold_au(amap, cur->au); + + // cur->au = NULL; // This value will be used for the next AU selection + cur->idx = amap->clusters_per_au; // AU closing + } + } + +} + + +/* Reposition target->idx for packing (Heuristics): + * Skip (num_to_skip) free clusters in (cur->au) + */ +static inline int amap_skip_cluster(struct super_block *sb, TARGET_AU_T *cur, int num_to_skip) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + u32 clu, read_clu; + MMSG_VAR(int num_to_skip_orig = num_to_skip); + + if (num_to_skip >= cur->au->free_clusters) { + EMSG("AMAP(%s): skip mis-use. amap_566\n", __func__); + return -EIO; + } + + clu = CLU_of_i_AU(amap, cur->au->idx, cur->idx); + while (num_to_skip > 0) { + if (clu >= CLUS_BASE) { + /* Cf. + * If AMAP's integrity is okay, + * we don't need to check if (clu < fsi->num_clusters) + */ + + if (fat_ent_get(sb, clu, &read_clu)) + return -EIO; + + if (IS_CLUS_FREE(read_clu)) + num_to_skip--; + } + + // Move clu->idx + clu++; + (cur->idx)++; + + if (cur->idx >= amap->clusters_per_au) { + /* End of AU (Not supposed) */ + EMSG("AMAP: Skip - End of AU?! (amap_596)\n"); + cur->idx = 0; + return -EIO; + } + } + + MMSG("AMAP: Skip_clusters (%d skipped => %d, among %d free clus)\n", + num_to_skip_orig, cur->idx, cur->au->free_clusters); + + return 0; +} + + +/* AMAP-based allocation function for FAT32 */ +s32 amap_fat_alloc_cluster(struct super_block *sb, u32 num_alloc, CHAIN_T *p_chain, s32 dest) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + TARGET_AU_T *cur = NULL; + AU_INFO_T *target_au = NULL; /* Allocation target AU */ + s32 ret = -ENOSPC; + u32 last_clu = CLUS_EOF, read_clu; + u32 new_clu, total_cnt; + u32 num_allocated = 0, num_allocated_each = 0; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + BUG_ON(!amap); + BUG_ON(IS_CLUS_EOF(fsi->used_clusters)); + + total_cnt = fsi->num_clusters - CLUS_BASE; + + if (unlikely(total_cnt < fsi->used_clusters)) { + sdfat_fs_error_ratelimit(sb, + "AMAP(%s): invalid used clusters(t:%u,u:%u)\n", + __func__, total_cnt, fsi->used_clusters); + return -EIO; + } + + if (num_alloc > total_cnt - fsi->used_clusters) + return -ENOSPC; + + p_chain->dir = CLUS_EOF; + + set_sb_dirty(sb); + + // spin_lock(&amap->amap_lock); + +retry_alloc: + /* Allocation strategy implemented */ + cur = amap_get_target_au(amap, dest, fsi->reserved_clusters); + if (unlikely(!cur)) { + // There is no available AU (only ignored-AU are left) + sdfat_msg(sb, KERN_ERR, "AMAP Allocator: no avaialble AU."); + goto error; + } + + /* If there are clusters to skip */ + if (cur->clu_to_skip > 0) { + if (amap_skip_cluster(sb, &amap->cur_cold, cur->clu_to_skip)) { + ret = -EIO; + goto error; + } + cur->clu_to_skip = 0; + } + + target_au = cur->au; + + /* + * cur->au : target AU info pointer + * cur->idx : the intra-cluster idx in the AU to start from + */ + BUG_ON(!cur->au); + BUG_ON(!cur->au->free_clusters); + BUG_ON(cur->idx >= amap->clusters_per_au); + + num_allocated_each = 0; + new_clu = CLU_of_i_AU(amap, target_au->idx, cur->idx); + + do { + /* Allocate at the target AU */ + if ((new_clu >= CLUS_BASE) && (new_clu < fsi->num_clusters)) { + if (fat_ent_get(sb, new_clu, &read_clu)) { + // spin_unlock(&amap->amap_lock); + ret = -EIO; + goto error; + } + + if (IS_CLUS_FREE(read_clu)) { + BUG_ON(GET_AU(amap, i_AU_of_CLU(amap, new_clu)) != target_au); + + /* Free cluster found */ + if (fat_ent_set(sb, new_clu, CLUS_EOF)) { + ret = -EIO; + goto error; + } + + num_allocated_each++; + + if (IS_CLUS_EOF(p_chain->dir)) { + p_chain->dir = new_clu; + } else { + if (fat_ent_set(sb, last_clu, new_clu)) { + ret = -EIO; + goto error; + } + } + last_clu = new_clu; + + /* Update au info */ + target_au->free_clusters--; + } + + } + + new_clu++; + (cur->idx)++; + + /* End of the AU */ + if ((cur->idx >= amap->clusters_per_au) || !(target_au->free_clusters)) + break; + } while (num_allocated_each < num_alloc); + + /* Update strategy info */ + amap_put_target_au(amap, cur, num_allocated_each); + + + num_allocated += num_allocated_each; + fsi->used_clusters += num_allocated_each; + num_alloc -= num_allocated_each; + + + if (num_alloc > 0) + goto retry_alloc; + + // spin_unlock(&amap->amap_lock); + return 0; +error: + if (num_allocated) + fsi->fs_func->free_cluster(sb, p_chain, 0); + return ret; +} + + +/* Free cluster for FAT32 (not implemented yet) */ +s32 amap_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + return -ENOTSUPP; +} + + +/* + * This is called by fat_free_cluster() + * to update AMAP info. + */ +s32 amap_release_cluster(struct super_block *sb, u32 clu) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + AU_INFO_T *au; + int i_au; + + // spin_lock(&amap->amap_lock); + + /* Update AU info */ + i_au = i_AU_of_CLU(amap, clu); + BUG_ON(i_au >= amap->n_au); + au = GET_AU(amap, i_au); + if (au->free_clusters >= amap->clusters_per_au) { + sdfat_fs_error(sb, "%s, au->free_clusters(%hd) is " + "greater than or equal to amap->clusters_per_au(%hd)", + __func__, au->free_clusters, amap->clusters_per_au); + return -EIO; + } + + if (IS_AU_HOT(au, amap)) { + MMSG("AMAP: Hot cluster freed\n"); + au->free_clusters++; + amap->total_fclu_hot++; + } else if (!IS_AU_WORKING(au, amap) && !IS_AU_IGNORED(au, amap)) { + /* Ordinary AU - update AU tree */ + // Can be optimized by implementing amap_update_au + amap_remove_cold_au(amap, au); + au->free_clusters++; + amap_add_cold_au(amap, au); + } else + au->free_clusters++; + + + /* Update AMAP info */ + if (au->free_clusters == amap->clusters_per_au) + amap->n_clean_au++; + if (au->free_clusters == 1) + amap->n_full_au--; + + // spin_unlock(&amap->amap_lock); + return 0; +} + + +/* + * Check if the cluster is in a working AU + * The caller should hold sb lock. + * This func. should be used only if smart allocation is on + */ +s32 amap_check_working(struct super_block *sb, u32 clu) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + AU_INFO_T *au; + + BUG_ON(!amap); + au = GET_AU(amap, i_AU_of_CLU(amap, clu)); + return IS_AU_WORKING(au, amap); +} + + +/* + * Return the # of free clusters in that AU + */ +s32 amap_get_freeclus(struct super_block *sb, u32 clu) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + AU_INFO_T *au; + + BUG_ON(!amap); + au = GET_AU(amap, i_AU_of_CLU(amap, clu)); + return (s32)au->free_clusters; +} + + +/* + * Add the AU containing 'clu' to the ignored AU list. + * The AU will not be used by the allocator. + * + * XXX: Ignored counter needed + */ +s32 amap_mark_ignore(struct super_block *sb, u32 clu) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + AU_INFO_T *au; + + BUG_ON(!amap); + au = GET_AU(amap, i_AU_of_CLU(amap, clu)); + + if (IS_AU_HOT(au, amap)) { + /* Doesn't work with hot AUs */ + return -EPERM; + } else if (IS_AU_WORKING(au, amap)) { + return -EBUSY; + } + + //BUG_ON(IS_AU_IGNORED(au, amap) && (GET_IGN_CNT(au) == 0)); + if (IS_AU_IGNORED(au, amap)) + return 0; + + amap_remove_cold_au(amap, au); + amap_insert_to_list(au, &amap->slist_ignored); + + BUG_ON(!IS_AU_IGNORED(au, amap)); + + //INC_IGN_CNT(au); + MMSG("AMAP: Mark ignored AU (%d)\n", au->idx); + return 0; +} + + +/* + * This function could be used only on IGNORED AUs. + * The caller should care whether it's ignored or not before using this func. + */ +s32 amap_unmark_ignore(struct super_block *sb, u32 clu) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + AU_INFO_T *au; + + BUG_ON(!amap); + + au = GET_AU(amap, i_AU_of_CLU(amap, clu)); + + BUG_ON(!IS_AU_IGNORED(au, amap)); + // BUG_ON(GET_IGN_CNT(au) == 0); + + amap_remove_from_list(au, &amap->slist_ignored); + amap_add_cold_au(amap, au); + + BUG_ON(IS_AU_IGNORED(au, amap)); + + //DEC_IGN_CNT(au); + + MMSG("AMAP: Unmark ignored AU (%d)\n", au->idx); + + return 0; +} + +/* + * Unmark all ignored AU + * This will return # of unmarked AUs + */ +s32 amap_unmark_ignore_all(struct super_block *sb) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + struct slist_head *entry; + AU_INFO_T *au; + int n = 0; + + BUG_ON(!amap); + entry = amap->slist_ignored.next; + while (entry) { + au = list_entry(entry, AU_INFO_T, shead); + + BUG_ON(au != GET_AU(amap, au->idx)); + BUG_ON(!IS_AU_IGNORED(au, amap)); + + //CLEAR_IGN_CNT(au); + amap_remove_from_list(au, &amap->slist_ignored); + amap_add_cold_au(amap, au); + + MMSG("AMAP: Unmark ignored AU (%d)\n", au->idx); + n++; + + entry = amap->slist_ignored.next; + } + + BUG_ON(amap->slist_ignored.next != NULL); + MMSG("AMAP: unmark_ignore_all, total %d AUs\n", n); + + return n; +} + +/** + * @fn amap_get_au_stat + * @brief report AUs status depending on mode + * @return positive on success, 0 otherwise + * @param sbi super block info + * @param mode TOTAL, CLEAN and FULL + */ +u32 amap_get_au_stat(struct super_block *sb, s32 mode) +{ + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + + if (!amap) + return 0; + + if (mode == VOL_AU_STAT_TOTAL) + return amap->n_au; + else if (mode == VOL_AU_STAT_CLEAN) + return amap->n_clean_au; + else if (mode == VOL_AU_STAT_FULL) + return amap->n_full_au; + + return 0; +} + diff --git a/fs/sdfat/amap_smart.h b/fs/sdfat/amap_smart.h new file mode 100644 index 0000000000000..caee6f6b3681d --- /dev/null +++ b/fs/sdfat/amap_smart.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _SDFAT_AMAP_H +#define _SDFAT_AMAP_H + +#include +#include +#include + +/* AMAP Configuration Variable */ +#define SMART_ALLOC_N_HOT_AU (5) + +/* Allocating Destination (for smart allocator): + * moved to sdfat.h + */ +/* + * #define ALLOC_COLD_ALIGNED (1) + * #define ALLOC_COLD_PACKING (2) + * #define ALLOC_COLD_SEQ (4) + */ + +/* Minimum sectors for support AMAP create */ +#define AMAP_MIN_SUPPORT_SECTORS (1048576) + +#define amap_add_hot_au(amap, au) amap_insert_to_list(au, &amap->slist_hot) + +/* singly linked list */ +struct slist_head { + struct slist_head *next; + struct slist_head *head; +}; + +/* AU entry type */ +typedef struct __AU_INFO_T { + uint16_t idx; /* the index of the AU (0, 1, 2, ... ) */ + uint16_t free_clusters; /* # of available cluster */ + union { + struct list_head head; + struct slist_head shead;/* singly linked list head for hot list */ + }; +} AU_INFO_T; + + +/* Allocation Target AU */ +typedef struct __TARGET_AU_T { + AU_INFO_T *au; /* Working AU */ + uint16_t idx; /* Intra-AU cluster index */ + uint16_t clu_to_skip; /* Clusters to skip */ +} TARGET_AU_T; + + +/* AMAP free-clusters-based node */ +typedef struct { + struct list_head head; /* the list of AUs */ +} FCLU_NODE_T; + + +/* AMAP options */ +typedef struct { + unsigned int packing_ratio; /* Tunable packing ratio */ + unsigned int au_size; /* AU size in sectors */ + unsigned int au_align_factor; /* Hidden sectors % au_size */ +} AMAP_OPT_T; + +typedef struct __AMAP_T { + spinlock_t amap_lock; /* obsolete */ + struct super_block *sb; + + int n_au; + int n_clean_au, n_full_au; + int clu_align_bias; + uint16_t clusters_per_au; + AU_INFO_T **au_table; /* An array of AU_INFO entries */ + AMAP_OPT_T option; + + /* Size-based AU management pool (cold) */ + FCLU_NODE_T *fclu_nodes; /* An array of listheads */ + int fclu_order; /* Page order that fclu_nodes needs */ + int fclu_hint; /* maximum # of free clusters in an AU */ + + /* Hot AU list */ + unsigned int total_fclu_hot; /* Free clusters in hot list */ + struct slist_head slist_hot; /* Hot AU list */ + + /* Ignored AU list */ + struct slist_head slist_ignored; + + /* Allocator variables (keep 2 AUs at maximum) */ + TARGET_AU_T cur_cold; + TARGET_AU_T cur_hot; + int n_need_packing; +} AMAP_T; + + +/* AU table */ +#define N_AU_PER_TABLE (int)(PAGE_SIZE / sizeof(AU_INFO_T)) +#define GET_AU(amap, i_AU) (amap->au_table[(i_AU) / N_AU_PER_TABLE] + ((i_AU) % N_AU_PER_TABLE)) +//#define MAX_CLU_PER_AU (int)(PAGE_SIZE / sizeof(FCLU_NODE_T)) +#define MAX_CLU_PER_AU (1024) + +/* Cold AU bucket <-> # of freeclusters */ +#define NODE_CLEAN(amap) (&amap->fclu_nodes[amap->clusters_per_au - 1]) +#define NODE(fclu, amap) (&amap->fclu_nodes[fclu - 1]) +#define FREE_CLUSTERS(node, amap) ((int)(node - amap->fclu_nodes) + 1) + +/* AU status */ +#define MAGIC_WORKING ((struct slist_head *)0xFFFF5091) +#define IS_AU_HOT(au, amap) (au->shead.head == &amap->slist_hot) +#define IS_AU_IGNORED(au, amap) (au->shead.head == &amap->slist_ignored) +#define IS_AU_WORKING(au, amap) (au->shead.head == MAGIC_WORKING) +#define SET_AU_WORKING(au) (au->shead.head = MAGIC_WORKING) + +/* AU <-> cluster */ +#define i_AU_of_CLU(amap, clu) ((amap->clu_align_bias + clu) / amap->clusters_per_au) +#define CLU_of_i_AU(amap, i_au, idx) \ + ((uint32_t)(i_au) * (uint32_t)amap->clusters_per_au + (idx) - amap->clu_align_bias) + +/* + * NOTE : AMAP internal functions are moved to core.h + */ + +#endif /* _SDFAT_AMAP_H */ diff --git a/fs/sdfat/api.c b/fs/sdfat/api.c new file mode 100644 index 0000000000000..02e0939efeecd --- /dev/null +++ b/fs/sdfat/api.c @@ -0,0 +1,629 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : sdfat_api.c */ +/* PURPOSE : sdFAT volume lock layer */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include + +#include "version.h" +#include "config.h" + +#include "sdfat.h" +#include "core.h" + +/*----------------------------------------------------------------------*/ +/* Internal structures */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ +static DEFINE_MUTEX(_lock_core); + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Global Function Definitions */ +/* - All functions for global use have same return value format, */ +/* that is, 0 on success and minus error number on */ +/* various error condition. */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* sdFAT Filesystem Init & Exit Functions */ +/*----------------------------------------------------------------------*/ + +s32 fsapi_init(void) +{ + return fscore_init(); +} + +s32 fsapi_shutdown(void) +{ + return fscore_shutdown(); +} + +/*----------------------------------------------------------------------*/ +/* Volume Management Functions */ +/*----------------------------------------------------------------------*/ + +/* mount the file system volume */ +s32 fsapi_mount(struct super_block *sb) +{ + s32 err; + + /* acquire the core lock for file system ccritical section */ + mutex_lock(&_lock_core); + + err = meta_cache_init(sb); + if (err) + goto out; + + err = fscore_mount(sb); +out: + if (err) + meta_cache_shutdown(sb); + + /* release the core lock for file system critical section */ + mutex_unlock(&_lock_core); + + return err; +} +EXPORT_SYMBOL(fsapi_mount); + +/* unmount the file system volume */ +s32 fsapi_umount(struct super_block *sb) +{ + s32 err; + + /* acquire the core lock for file system ccritical section */ + mutex_lock(&_lock_core); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_umount(sb); + meta_cache_shutdown(sb); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + + /* release the core lock for file system critical section */ + mutex_unlock(&_lock_core); + + return err; +} +EXPORT_SYMBOL(fsapi_umount); + +/* get the information of a file system volume */ +s32 fsapi_statfs(struct super_block *sb, VOL_INFO_T *info) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* check the validity of pointer parameters */ + ASSERT(info); + + if (fsi->used_clusters == (u32) ~0) { + s32 err; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_statfs(sb, info); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; + } + + info->FatType = fsi->vol_type; + info->ClusterSize = fsi->cluster_size; + info->NumClusters = fsi->num_clusters - 2; /* clu 0 & 1 */ + info->UsedClusters = fsi->used_clusters + fsi->reserved_clusters; + info->FreeClusters = info->NumClusters - info->UsedClusters; + + return 0; +} +EXPORT_SYMBOL(fsapi_statfs); + +/* synchronize a file system volume */ +s32 fsapi_sync_fs(struct super_block *sb, s32 do_sync) +{ + s32 err; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_sync_fs(sb, do_sync); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_sync_fs); + +s32 fsapi_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync) +{ + s32 err; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_set_vol_flags(sb, new_flag, always_sync); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_set_vol_flags); + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* lookup */ +s32 fsapi_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && path); + + if (unlikely(!strlen(path))) + return -EINVAL; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_lookup(inode, path, fid); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_lookup); + +/* create a file */ +s32 fsapi_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && path); + + if (unlikely(!strlen(path))) + return -EINVAL; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_create(inode, path, mode, fid); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_create); + +/* read the target string of symlink */ +s32 fsapi_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && buffer); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_read_link(inode, fid, buffer, count, rcount); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_read_link); + +/* write the target string of symlink */ +s32 fsapi_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && buffer); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_write_link(inode, fid, buffer, count, wcount); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_write_link); + +/* resize the file length */ +s32 fsapi_truncate(struct inode *inode, u64 old_size, u64 new_size) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + TMSG("%s entered (inode %p size %llu)\n", __func__, inode, new_size); + err = fscore_truncate(inode, old_size, new_size); + TMSG("%s exitted (%d)\n", __func__, err); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_truncate); + +/* rename or move a old file into a new file */ +s32 fsapi_rename(struct inode *old_parent_inode, FILE_ID_T *fid, + struct inode *new_parent_inode, struct dentry *new_dentry) +{ + s32 err; + struct super_block *sb = old_parent_inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_rename(old_parent_inode, fid, new_parent_inode, new_dentry); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_rename); + +/* remove a file */ +s32 fsapi_remove(struct inode *inode, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_remove(inode, fid); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_remove); + +/* get the information of a given file */ +s32 fsapi_read_inode(struct inode *inode, DIR_ENTRY_T *info) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + TMSG("%s entered (inode %p info %p\n", __func__, inode, info); + err = fscore_read_inode(inode, info); + TMSG("%s exited (err:%d)\n", __func__, err); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_read_inode); + +/* set the information of a given file */ +s32 fsapi_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + TMSG("%s entered (inode %p info %p sync:%d\n", + __func__, inode, info, sync); + err = fscore_write_inode(inode, info, sync); + TMSG("%s exited (err:%d)\n", __func__, err); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_write_inode); + +/* return the cluster number in the given cluster offset */ +s32 fsapi_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(clu); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + TMSG("%s entered (inode:%p clus:%08x dest:%d\n", + __func__, inode, *clu, dest); + err = fscore_map_clus(inode, clu_offset, clu, dest); + TMSG("%s exited (clu:%08x err:%d)\n", __func__, *clu, err); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_map_clus); + +/* reserve a cluster */ +s32 fsapi_reserve_clus(struct inode *inode) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + TMSG("%s entered (inode:%p)\n", __func__, inode); + err = fscore_reserve_clus(inode); + TMSG("%s exited (err:%d)\n", __func__, err); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_reserve_clus); + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* create(make) a directory */ +s32 fsapi_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid && path); + + if (unlikely(!strlen(path))) + return -EINVAL; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_mkdir(inode, path, fid); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_mkdir); + +/* read a directory entry from the opened directory */ +s32 fsapi_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(dir_entry); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_readdir(inode, dir_entry); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_readdir); + +/* remove a directory */ +s32 fsapi_rmdir(struct inode *inode, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_rmdir(inode, fid); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_rmdir); + +/* unlink a file. + * that is, remove an entry from a directory. BUT don't truncate + */ +s32 fsapi_unlink(struct inode *inode, FILE_ID_T *fid) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(fid); + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = fscore_unlink(inode, fid); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_unlink); + +/* reflect the internal dirty flags to VFS bh dirty flags */ +s32 fsapi_cache_flush(struct super_block *sb, int do_sync) +{ + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + fcache_flush(sb, do_sync); + dcache_flush(sb, do_sync); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return 0; +} +EXPORT_SYMBOL(fsapi_cache_flush); + +/* release FAT & buf cache */ +s32 fsapi_cache_release(struct super_block *sb) +{ +#ifdef CONFIG_SDFAT_DEBUG + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + + fcache_release_all(sb); + dcache_release_all(sb); + + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); +#endif /* CONFIG_SDFAT_DEBUG */ + return 0; +} +EXPORT_SYMBOL(fsapi_cache_release); + +u32 fsapi_get_au_stat(struct super_block *sb, s32 mode) +{ + /* volume lock is not required */ + return fscore_get_au_stat(sb, mode); +} +EXPORT_SYMBOL(fsapi_get_au_stat); + +/* clear extent cache */ +void fsapi_invalidate_extent(struct inode *inode) +{ + /* Volume lock is not required, + * because it is only called by evict_inode. + * If any other function can call it, + * you should check whether volume lock is needed or not. + */ + extent_cache_inval_inode(inode); +} +EXPORT_SYMBOL(fsapi_invalidate_extent); + + + +#ifdef CONFIG_SDFAT_DFR +/*----------------------------------------------------------------------*/ +/* Defragmentation related */ +/*----------------------------------------------------------------------*/ +s32 fsapi_dfr_get_info(struct super_block *sb, void *arg) +{ + /* volume lock is not required */ + return defrag_get_info(sb, (struct defrag_info_arg *)arg); +} +EXPORT_SYMBOL(fsapi_dfr_get_info); + +s32 fsapi_dfr_scan_dir(struct super_block *sb, void *args) +{ + s32 err; + + /* check the validity of pointer parameters */ + ASSERT(args); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = defrag_scan_dir(sb, (struct defrag_trav_arg *)args); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_dfr_scan_dir); + +s32 fsapi_dfr_validate_clus(struct inode *inode, void *chunk, int skip_prev) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = defrag_validate_cluster(inode, + (struct defrag_chunk_info *)chunk, skip_prev); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_dfr_validate_clus); + +s32 fsapi_dfr_reserve_clus(struct super_block *sb, s32 nr_clus) +{ + s32 err; + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = defrag_reserve_clusters(sb, nr_clus); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + return err; +} +EXPORT_SYMBOL(fsapi_dfr_reserve_clus); + +s32 fsapi_dfr_mark_ignore(struct super_block *sb, unsigned int clus) +{ + /* volume lock is not required */ + return defrag_mark_ignore(sb, clus); +} +EXPORT_SYMBOL(fsapi_dfr_mark_ignore); + +void fsapi_dfr_unmark_ignore_all(struct super_block *sb) +{ + /* volume lock is not required */ + defrag_unmark_ignore_all(sb); +} +EXPORT_SYMBOL(fsapi_dfr_unmark_ignore_all); + +s32 fsapi_dfr_map_clus(struct inode *inode, u32 clu_offset, u32 *clu) +{ + s32 err; + struct super_block *sb = inode->i_sb; + + /* check the validity of pointer parameters */ + ASSERT(clu); + + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + err = defrag_map_cluster(inode, clu_offset, clu); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); + + return err; +} +EXPORT_SYMBOL(fsapi_dfr_map_clus); + +void fsapi_dfr_writepage_endio(struct page *page) +{ + /* volume lock is not required */ + defrag_writepage_end_io(page); +} +EXPORT_SYMBOL(fsapi_dfr_writepage_endio); + +void fsapi_dfr_update_fat_prev(struct super_block *sb, int force) +{ + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + defrag_update_fat_prev(sb, force); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); +} +EXPORT_SYMBOL(fsapi_dfr_update_fat_prev); + +void fsapi_dfr_update_fat_next(struct super_block *sb) +{ + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + defrag_update_fat_next(sb); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); +} +EXPORT_SYMBOL(fsapi_dfr_update_fat_next); + +void fsapi_dfr_check_discard(struct super_block *sb) +{ + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + defrag_check_discard(sb); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); +} +EXPORT_SYMBOL(fsapi_dfr_check_discard); + +void fsapi_dfr_free_clus(struct super_block *sb, u32 clus) +{ + mutex_lock(&(SDFAT_SB(sb)->s_vlock)); + defrag_free_cluster(sb, clus); + mutex_unlock(&(SDFAT_SB(sb)->s_vlock)); +} +EXPORT_SYMBOL(fsapi_dfr_free_clus); + +s32 fsapi_dfr_check_dfr_required(struct super_block *sb, int *totalau, int *cleanau, int *fullau) +{ + /* volume lock is not required */ + return defrag_check_defrag_required(sb, totalau, cleanau, fullau); +} +EXPORT_SYMBOL(fsapi_dfr_check_dfr_required); + +s32 fsapi_dfr_check_dfr_on(struct inode *inode, loff_t start, loff_t end, s32 cancel, const char *caller) +{ + /* volume lock is not required */ + return defrag_check_defrag_on(inode, start, end, cancel, caller); +} +EXPORT_SYMBOL(fsapi_dfr_check_dfr_on); + + + +#ifdef CONFIG_SDFAT_DFR_DEBUG +void fsapi_dfr_spo_test(struct super_block *sb, int flag, const char *caller) +{ + /* volume lock is not required */ + defrag_spo_test(sb, flag, caller); +} +EXPORT_SYMBOL(fsapi_dfr_spo_test); +#endif + + +#endif /* CONFIG_SDFAT_DFR */ + +/* end of sdfat_api.c */ diff --git a/fs/sdfat/api.h b/fs/sdfat/api.h new file mode 100644 index 0000000000000..0bd05d700c85e --- /dev/null +++ b/fs/sdfat/api.h @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _SDFAT_API_H +#define _SDFAT_API_H + +#include "config.h" +#include "sdfat_fs.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/*----------------------------------------------------------------------*/ +/* Configure Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ +/* cache size (in number of sectors) */ +/* (should be an exponential value of 2) */ +#define FAT_CACHE_SIZE 128 +#define FAT_CACHE_HASH_SIZE 64 +#define BUF_CACHE_SIZE 256 +#define BUF_CACHE_HASH_SIZE 64 + +/* Read-ahead related */ +/* First config vars. should be pow of 2 */ +#define FCACHE_MAX_RA_SIZE (PAGE_SIZE) +#define DCACHE_MAX_RA_SIZE (128*1024) + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ +/* type values */ +#define TYPE_UNUSED 0x0000 +#define TYPE_DELETED 0x0001 +#define TYPE_INVALID 0x0002 +#define TYPE_CRITICAL_PRI 0x0100 +#define TYPE_BITMAP 0x0101 +#define TYPE_UPCASE 0x0102 +#define TYPE_VOLUME 0x0103 +#define TYPE_DIR 0x0104 +#define TYPE_FILE 0x011F +#define TYPE_SYMLINK 0x015F +#define TYPE_CRITICAL_SEC 0x0200 +#define TYPE_STREAM 0x0201 +#define TYPE_EXTEND 0x0202 +#define TYPE_ACL 0x0203 +#define TYPE_BENIGN_PRI 0x0400 +#define TYPE_GUID 0x0401 +#define TYPE_PADDING 0x0402 +#define TYPE_ACLTAB 0x0403 +#define TYPE_BENIGN_SEC 0x0800 +#define TYPE_ALL 0x0FFF + +/* eio values */ +#define SDFAT_EIO_NONE (0x00000000) +#define SDFAT_EIO_READ (0x00000001) +#define SDFAT_EIO_WRITE (0x00000002) +#define SDFAT_EIO_BDI (0x00000004) + +/* modes for volume allocation unit status */ +#define VOL_AU_STAT_TOTAL (0) +#define VOL_AU_STAT_CLEAN (1) +#define VOL_AU_STAT_FULL (2) + +/*----------------------------------------------------------------------*/ +/* NLS Type Definitions */ +/*----------------------------------------------------------------------*/ + +/* DOS name structure */ +typedef struct { + u8 name[DOS_NAME_LENGTH]; + u8 name_case; +} DOS_NAME_T; + +/* unicode name structure */ +typedef struct { + u16 name[MAX_NAME_LENGTH+3]; /* +3 for null and for converting */ + u16 name_hash; + u8 name_len; +} UNI_NAME_T; + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ +/* should be merged it to DATE_TIME_T */ +typedef struct { + u16 sec; /* 0 ~ 59 */ + u16 min; /* 0 ~ 59 */ + u16 hour; /* 0 ~ 23 */ + u16 day; /* 1 ~ 31 */ + u16 mon; /* 1 ~ 12 */ + u16 year; /* 0 ~ 127 (since 1980) */ +} TIMESTAMP_T; + + +typedef struct { + u16 Year; + u16 Month; + u16 Day; + u16 Hour; + u16 Minute; + u16 Second; + u16 MilliSecond; +} DATE_TIME_T; + +typedef struct { + u64 Offset; // start sector number of the partition + u64 Size; // in sectors +} PART_INFO_T; + +typedef struct { + u32 SecSize; // sector size in bytes + u64 DevSize; // block device size in sectors +} DEV_INFO_T; + +typedef struct { + u32 FatType; + u32 ClusterSize; + u32 NumClusters; + u32 FreeClusters; + u32 UsedClusters; +} VOL_INFO_T; + +/* directory structure */ +typedef struct { + u32 dir; + u32 size; + u8 flags; +} CHAIN_T; + +/* hint structure */ +typedef struct { + u32 clu; + union { + u32 off; // cluster offset + s32 eidx; // entry index + }; +} HINT_T; + +typedef struct { + spinlock_t cache_lru_lock; + struct list_head cache_lru; + s32 nr_caches; + u32 cache_valid_id; // for avoiding the race between alloc and free +} EXTENT_T; + +/* first empty entry hint information */ +typedef struct { + s32 eidx; // entry index of a directory + s32 count; // count of continuous empty entry + CHAIN_T cur; // the cluster that first empty slot exists in +} HINT_FEMP_T; + +/* file id structure */ +typedef struct { + CHAIN_T dir; + s32 entry; + u32 type; + u32 attr; + u32 start_clu; + u64 size; + u8 flags; + u8 reserved[3]; // padding + u32 version; // the copy of low 32bit of i_version to check the validation of hint_stat + s64 rwoffset; // file offset or dentry index for readdir + EXTENT_T extent; // extent cache for a file + HINT_T hint_bmap; // hint for cluster last accessed + HINT_T hint_stat; // hint for entry index we try to lookup next time + HINT_FEMP_T hint_femp; // hint for first empty entry +} FILE_ID_T; + +typedef struct { + s8 *lfn; + s8 *sfn; + s32 lfnbuf_len; //usally MAX_UNINAME_BUF_SIZE + s32 sfnbuf_len; //usally MAX_DOSNAME_BUF_SIZE, used only for vfat, not for exfat +} DENTRY_NAMEBUF_T; + +typedef struct { + u32 Attr; + u64 Size; + u32 NumSubdirs; + DATE_TIME_T CreateTimestamp; + DATE_TIME_T ModifyTimestamp; + DATE_TIME_T AccessTimestamp; + DENTRY_NAMEBUF_T NameBuf; +} DIR_ENTRY_T; + +/* cache information */ +typedef struct __cache_entry { + struct __cache_entry *next; + struct __cache_entry *prev; + struct { + struct __cache_entry *next; + struct __cache_entry *prev; + } hash; + u64 sec; + u32 flag; + struct buffer_head *bh; +} cache_ent_t; + +/*----------------------------------------------------------------------*/ +/* Type Definitions : Wrapper & In-Core */ +/*----------------------------------------------------------------------*/ +typedef struct __FATENT_OPS_T { + s32 (*ent_get)(struct super_block *sb, u32 loc, u32 *content); + s32 (*ent_set)(struct super_block *sb, u32 loc, u32 content); +} FATENT_OPS_T; + +typedef struct { + s32 (*alloc_cluster)(struct super_block *, u32, CHAIN_T *, s32); + s32 (*free_cluster)(struct super_block *, CHAIN_T *, s32); + s32 (*count_used_clusters)(struct super_block *, u32 *); + s32 (*init_dir_entry)(struct super_block *, CHAIN_T *, s32, u32, u32, u64); + s32 (*init_ext_entry)(struct super_block *, CHAIN_T *, s32, s32, UNI_NAME_T *, DOS_NAME_T *); + s32 (*find_dir_entry)(struct super_block *, FILE_ID_T *, CHAIN_T *, UNI_NAME_T *, s32, DOS_NAME_T *, u32); + s32 (*delete_dir_entry)(struct super_block *, CHAIN_T *, s32, s32, s32); + void (*get_uniname_from_ext_entry)(struct super_block *, CHAIN_T *, s32, u16 *); + s32 (*count_ext_entries)(struct super_block *, CHAIN_T *, s32, DENTRY_T *); + s32 (*calc_num_entries)(UNI_NAME_T *); + s32 (*check_max_dentries)(FILE_ID_T *); + u32 (*get_entry_type)(DENTRY_T *); + void (*set_entry_type)(DENTRY_T *, u32); + u32 (*get_entry_attr)(DENTRY_T *); + void (*set_entry_attr)(DENTRY_T *, u32); + u8 (*get_entry_flag)(DENTRY_T *); + void (*set_entry_flag)(DENTRY_T *, u8); + u32 (*get_entry_clu0)(DENTRY_T *); + void (*set_entry_clu0)(DENTRY_T *, u32); + u64 (*get_entry_size)(DENTRY_T *); + void (*set_entry_size)(DENTRY_T *, u64); + void (*get_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8); + void (*set_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8); + u32 (*get_au_stat)(struct super_block *, s32); +} FS_FUNC_T; + +typedef struct __FS_INFO_T { + s32 bd_opened; // opened or not + u32 vol_type; // volume FAT type + u32 vol_id; // volume serial number + u64 num_sectors; // num of sectors in volume + u32 num_clusters; // num of clusters in volume + u32 cluster_size; // cluster size in bytes + u32 cluster_size_bits; + u32 sect_per_clus; // cluster size in sectors + u32 sect_per_clus_bits; + u64 FAT1_start_sector; // FAT1 start sector + u64 FAT2_start_sector; // FAT2 start sector + u64 root_start_sector; // root dir start sector + u64 data_start_sector; // data area start sector + u32 num_FAT_sectors; // num of FAT sectors + u32 root_dir; // root dir cluster + u32 dentries_in_root; // num of dentries in root dir + u32 dentries_per_clu; // num of dentries per cluster + u32 vol_flag; // volume dirty flag + struct buffer_head *pbr_bh; // buffer_head of PBR sector + + u32 map_clu; // allocation bitmap start cluster + u32 map_sectors; // num of allocation bitmap sectors + struct buffer_head **vol_amap; // allocation bitmap + + u16 **vol_utbl; // upcase table + + u32 clu_srch_ptr; // cluster search pointer + u32 used_clusters; // number of used clusters + + u32 prev_eio; // block device operation error flag + + FS_FUNC_T *fs_func; + FATENT_OPS_T *fatent_ops; + + s32 reserved_clusters; // # of reserved clusters (DA) + void *amap; // AU Allocation Map + + /* fat cache */ + struct { + cache_ent_t pool[FAT_CACHE_SIZE]; + cache_ent_t lru_list; + cache_ent_t hash_list[FAT_CACHE_HASH_SIZE]; + } fcache; + + /* meta cache */ + struct { + cache_ent_t pool[BUF_CACHE_SIZE]; + cache_ent_t lru_list; + cache_ent_t keep_list; // CACHEs in this list will not be kicked by normal lru operations + cache_ent_t hash_list[BUF_CACHE_HASH_SIZE]; + } dcache; +} FS_INFO_T; + +/*======================================================================*/ +/* */ +/* API FUNCTION DECLARATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* file system initialization & shutdown functions */ +s32 fsapi_init(void); +s32 fsapi_shutdown(void); + +/* volume management functions */ +s32 fsapi_mount(struct super_block *sb); +s32 fsapi_umount(struct super_block *sb); +s32 fsapi_statfs(struct super_block *sb, VOL_INFO_T *info); +s32 fsapi_sync_fs(struct super_block *sb, s32 do_sync); +s32 fsapi_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync); + +/* file management functions */ +s32 fsapi_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid); +s32 fsapi_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid); +s32 fsapi_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); +s32 fsapi_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); +s32 fsapi_remove(struct inode *inode, FILE_ID_T *fid); /* unlink and truncate */ +s32 fsapi_truncate(struct inode *inode, u64 old_size, u64 new_size); +s32 fsapi_rename(struct inode *old_parent_inode, FILE_ID_T *fid, + struct inode *new_parent_inode, struct dentry *new_dentry); +s32 fsapi_unlink(struct inode *inode, FILE_ID_T *fid); +s32 fsapi_read_inode(struct inode *inode, DIR_ENTRY_T *info); +s32 fsapi_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync); +s32 fsapi_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest); +s32 fsapi_reserve_clus(struct inode *inode); + +/* directory management functions */ +s32 fsapi_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid); +s32 fsapi_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry); +s32 fsapi_rmdir(struct inode *inode, FILE_ID_T *fid); + +/* FAT & buf cache functions */ +s32 fsapi_cache_flush(struct super_block *sb, int do_sync); +s32 fsapi_cache_release(struct super_block *sb); + +/* extra info functions */ +u32 fsapi_get_au_stat(struct super_block *sb, s32 mode); + +/* extent cache functions */ +void fsapi_invalidate_extent(struct inode *inode); + +#ifdef CONFIG_SDFAT_DFR +/*----------------------------------------------------------------------*/ +/* Defragmentation related */ +/*----------------------------------------------------------------------*/ + +s32 fsapi_dfr_get_info(struct super_block *sb, void *arg); + +s32 fsapi_dfr_scan_dir(struct super_block *sb, void *args); + +s32 fsapi_dfr_validate_clus(struct inode *inode, void *chunk, int skip_prev); +s32 fsapi_dfr_reserve_clus(struct super_block *sb, s32 nr_clus); +s32 fsapi_dfr_mark_ignore(struct super_block *sb, unsigned int clus); +void fsapi_dfr_unmark_ignore_all(struct super_block *sb); + +s32 fsapi_dfr_map_clus(struct inode *inode, u32 clu_offset, u32 *clu); +void fsapi_dfr_writepage_endio(struct page *page); + +void fsapi_dfr_update_fat_prev(struct super_block *sb, int force); +void fsapi_dfr_update_fat_next(struct super_block *sb); +void fsapi_dfr_check_discard(struct super_block *sb); +void fsapi_dfr_free_clus(struct super_block *sb, u32 clus); + +s32 fsapi_dfr_check_dfr_required(struct super_block *sb, int *totalau, int *cleanau, int *fullau); +s32 fsapi_dfr_check_dfr_on(struct inode *inode, loff_t start, loff_t end, s32 cancel, const char *caller); + + +#ifdef CONFIG_SDFAT_DFR_DEBUG +void fsapi_dfr_spo_test(struct super_block *sb, int flag, const char *caller); +#endif /* CONFIG_SDFAT_DFR_DEBUG */ + +#endif /* CONFIG_SDFAT_DFR */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _SDFAT_API_H */ + +/* end of api.h */ diff --git a/fs/sdfat/blkdev.c b/fs/sdfat/blkdev.c new file mode 100644 index 0000000000000..264c670df0f0d --- /dev/null +++ b/fs/sdfat/blkdev.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : blkdev.c */ +/* PURPOSE : sdFAT Block Device Driver Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/************************************************************************/ + +#include +#include +#include + +#include "sdfat.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY */ +/************************************************************************/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + /* EMPTY */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) */ +static struct backing_dev_info *inode_to_bdi(struct inode *bd_inode) +{ + return bd_inode->i_mapping->backing_dev_info; +} +#endif + +/*======================================================================*/ +/* Function Definitions */ +/*======================================================================*/ +s32 bdev_open_dev(struct super_block *sb) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (fsi->bd_opened) + return 0; + + fsi->bd_opened = true; + return 0; +} + +s32 bdev_close_dev(struct super_block *sb) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + fsi->bd_opened = false; + return 0; +} + +static inline s32 block_device_ejected(struct super_block *sb) +{ + struct inode *bd_inode = sb->s_bdev->bd_inode; + struct backing_dev_info *bdi = inode_to_bdi(bd_inode); + + return (bdi->dev == NULL); +} + +s32 bdev_check_bdi_valid(struct super_block *sb) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (block_device_ejected(sb)) { + if (!(fsi->prev_eio & SDFAT_EIO_BDI)) { + fsi->prev_eio |= SDFAT_EIO_BDI; + sdfat_log_msg(sb, KERN_ERR, "%s: block device is " + "eliminated.(bdi:%p)", __func__, sb->s_bdi); + sdfat_debug_warn_on(1); + } + return -ENXIO; + } + + return 0; +} + + +/* Make a readahead request */ +s32 bdev_readahead(struct super_block *sb, u64 secno, u64 num_secs) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u32 sects_per_page = (PAGE_SIZE >> sb->s_blocksize_bits); + struct blk_plug plug; + u64 i; + + if (!fsi->bd_opened) + return -EIO; + + blk_start_plug(&plug); + for (i = 0; i < num_secs; i++) { + if (i && !(i & (sects_per_page - 1))) + blk_flush_plug(current); + sb_breadahead(sb, (sector_t)(secno + i)); + } + blk_finish_plug(&plug); + + return 0; +} + +s32 bdev_mread(struct super_block *sb, u64 secno, struct buffer_head **bh, u64 num_secs, s32 read) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u8 blksize_bits = sb->s_blocksize_bits; +#ifdef CONFIG_SDFAT_DBG_IOCTL + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & SDFAT_DEBUGFLAGS_ERROR_RW) + return -EIO; +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + + if (!fsi->bd_opened) + return -EIO; + + brelse(*bh); + + if (read) + *bh = __bread(sb->s_bdev, (sector_t)secno, num_secs << blksize_bits); + else + *bh = __getblk(sb->s_bdev, (sector_t)secno, num_secs << blksize_bits); + + /* read successfully */ + if (*bh) + return 0; + + /* + * patch 1.2.4 : reset ONCE warning message per volume. + */ + if (!(fsi->prev_eio & SDFAT_EIO_READ)) { + fsi->prev_eio |= SDFAT_EIO_READ; + sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__); + sdfat_debug_warn_on(1); + } + + return -EIO; +} + +s32 bdev_mwrite(struct super_block *sb, u64 secno, struct buffer_head *bh, u64 num_secs, s32 sync) +{ + u64 count; + struct buffer_head *bh2; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); +#ifdef CONFIG_SDFAT_DBG_IOCTL + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & SDFAT_DEBUGFLAGS_ERROR_RW) + return -EIO; +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + + if (!fsi->bd_opened) + return -EIO; + + if (secno == bh->b_blocknr) { + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + if (sync && (sync_dirty_buffer(bh) != 0)) + return -EIO; + } else { + count = num_secs << sb->s_blocksize_bits; + + bh2 = __getblk(sb->s_bdev, (sector_t)secno, count); + + if (!bh2) + goto no_bh; + + lock_buffer(bh2); + memcpy(bh2->b_data, bh->b_data, count); + set_buffer_uptodate(bh2); + mark_buffer_dirty(bh2); + unlock_buffer(bh2); + if (sync && (sync_dirty_buffer(bh2) != 0)) { + __brelse(bh2); + goto no_bh; + } + __brelse(bh2); + } + return 0; +no_bh: + /* + * patch 1.2.4 : reset ONCE warning message per volume. + */ + if (!(fsi->prev_eio & SDFAT_EIO_WRITE)) { + fsi->prev_eio |= SDFAT_EIO_WRITE; + sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__); + sdfat_debug_warn_on(1); + } + + return -EIO; +} + +s32 bdev_sync_all(struct super_block *sb) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); +#ifdef CONFIG_SDFAT_DBG_IOCTL + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & SDFAT_DEBUGFLAGS_ERROR_RW) + return -EIO; +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + + if (!fsi->bd_opened) + return -EIO; + + return sync_blockdev(sb->s_bdev); +} + +/* + * Sector Read/Write Functions + */ +s32 read_sect(struct super_block *sb, u64 sec, struct buffer_head **bh, s32 read) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + BUG_ON(!bh); + if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) { + sdfat_fs_error_ratelimit(sb, + "%s: out of range (sect:%llu)", __func__, sec); + return -EIO; + } + + if (bdev_mread(sb, sec, bh, 1, read)) { + sdfat_fs_error_ratelimit(sb, + "%s: I/O error (sect:%llu)", __func__, sec); + return -EIO; + } + + return 0; +} + +s32 write_sect(struct super_block *sb, u64 sec, struct buffer_head *bh, s32 sync) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + BUG_ON(!bh); + if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) { + sdfat_fs_error_ratelimit(sb, + "%s: out of range (sect:%llu)", __func__, sec); + return -EIO; + } + + if (bdev_mwrite(sb, sec, bh, 1, sync)) { + sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%llu)", + __func__, sec); + return -EIO; + } + + return 0; +} + +s32 read_msect(struct super_block *sb, u64 sec, struct buffer_head **bh, u64 num_secs, s32 read) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + BUG_ON(!bh); + if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) { + sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%llu len:%llu)", + __func__, sec, num_secs); + return -EIO; + } + + if (bdev_mread(sb, sec, bh, num_secs, read)) { + sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%llu len:%llu)", + __func__, sec, num_secs); + return -EIO; + } + + return 0; +} + +s32 write_msect(struct super_block *sb, u64 sec, struct buffer_head *bh, u64 num_secs, s32 sync) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + BUG_ON(!bh); + if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) { + sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%llu len:%llu)", + __func__, sec, num_secs); + return -EIO; + } + + + if (bdev_mwrite(sb, sec, bh, num_secs, sync)) { + sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%llu len:%llu)", + __func__, sec, num_secs); + return -EIO; + } + + return 0; +} + +static inline void __blkdev_write_bhs(struct buffer_head **bhs, s32 nr_bhs) +{ + s32 i; + + for (i = 0; i < nr_bhs; i++) + write_dirty_buffer(bhs[i], WRITE); +} + +static inline s32 __blkdev_sync_bhs(struct buffer_head **bhs, s32 nr_bhs) +{ + s32 i, err = 0; + + for (i = 0; i < nr_bhs; i++) { + wait_on_buffer(bhs[i]); + if (!err && !buffer_uptodate(bhs[i])) + err = -EIO; + } + return err; +} + +static inline s32 __buffer_zeroed(struct super_block *sb, u64 blknr, u64 num_secs) +{ + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + s32 nr_bhs = MAX_BUF_PER_PAGE; + u64 last_blknr = blknr + num_secs; + s32 err, i, n; + struct blk_plug plug; + + /* Zeroing the unused blocks on this cluster */ + n = 0; + blk_start_plug(&plug); + while (blknr < last_blknr) { + bhs[n] = sb_getblk(sb, (sector_t)blknr); + if (!bhs[n]) { + err = -ENOMEM; + blk_finish_plug(&plug); + goto error; + } + memset(bhs[n]->b_data, 0, sb->s_blocksize); + set_buffer_uptodate(bhs[n]); + mark_buffer_dirty(bhs[n]); + + n++; + blknr++; + + if (blknr == last_blknr) + break; + + if (n == nr_bhs) { + __blkdev_write_bhs(bhs, n); + + for (i = 0; i < n; i++) + brelse(bhs[i]); + n = 0; + } + } + __blkdev_write_bhs(bhs, n); + blk_finish_plug(&plug); + + err = __blkdev_sync_bhs(bhs, n); + if (err) + goto error; + + for (i = 0; i < n; i++) + brelse(bhs[i]); + + return 0; + +error: + EMSG("%s: failed zeroed sect %llu\n", __func__, blknr); + for (i = 0; i < n; i++) + bforget(bhs[i]); + + return err; +} + +s32 write_msect_zero(struct super_block *sb, u64 sec, u64 num_secs) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) { + sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%llu len:%llu)", + __func__, sec, num_secs); + return -EIO; + } + + /* Just return -EAGAIN if it is failed */ + if (__buffer_zeroed(sb, sec, num_secs)) + return -EAGAIN; + + return 0; +} /* end of write_msect_zero */ + +/* end of blkdev.c */ diff --git a/fs/sdfat/cache.c b/fs/sdfat/cache.c new file mode 100644 index 0000000000000..f0170c5602075 --- /dev/null +++ b/fs/sdfat/cache.c @@ -0,0 +1,842 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : cache.c */ +/* PURPOSE : sdFAT Cache Manager */ +/* (FAT Cache & Buffer Cache) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include /* for mark_page_accessed() */ +#include + +#include "sdfat.h" +#include "core.h" + +#define DEBUG_HASH_LIST +#define DEBUG_HASH_PREV (0xAAAA5555) +#define DEBUG_HASH_NEXT (0x5555AAAA) + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ +/* All buffer structures are protected w/ fsi->v_sem */ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ +#define LOCKBIT (0x01) +#define DIRTYBIT (0x02) +#define KEEPBIT (0x04) + +/*----------------------------------------------------------------------*/ +/* Cache handling function declarations */ +/*----------------------------------------------------------------------*/ +static cache_ent_t *__fcache_find(struct super_block *sb, u64 sec); +static cache_ent_t *__fcache_get(struct super_block *sb); +static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp); +static void __fcache_remove_hash(cache_ent_t *bp); + +static cache_ent_t *__dcache_find(struct super_block *sb, u64 sec); +static cache_ent_t *__dcache_get(struct super_block *sb); +static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp); +static void __dcache_remove_hash(cache_ent_t *bp); + +/*----------------------------------------------------------------------*/ +/* Static functions */ +/*----------------------------------------------------------------------*/ +static void push_to_mru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->next = list->next; + bp->prev = list; + list->next->prev = bp; + list->next = bp; +} + +static void push_to_lru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->prev = list->prev; + bp->next = list; + list->prev->next = bp; + list->prev = bp; +} + +static void move_to_mru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_mru(bp, list); +} + +static void move_to_lru(cache_ent_t *bp, cache_ent_t *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_lru(bp, list); +} + +static inline s32 __check_hash_valid(cache_ent_t *bp) +{ +#ifdef DEBUG_HASH_LIST + if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) || + (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) { + return -EINVAL; + } +#endif + if ((bp->hash.next == bp) || (bp->hash.prev == bp)) + return -EINVAL; + + return 0; +} + +static inline void __remove_from_hash(cache_ent_t *bp) +{ + (bp->hash.prev)->hash.next = bp->hash.next; + (bp->hash.next)->hash.prev = bp->hash.prev; + bp->hash.next = bp; + bp->hash.prev = bp; +#ifdef DEBUG_HASH_LIST + bp->hash.next = (cache_ent_t *)DEBUG_HASH_NEXT; + bp->hash.prev = (cache_ent_t *)DEBUG_HASH_PREV; +#endif +} + +/* Do FAT mirroring (don't sync) + * sec: sector No. in FAT1 + * bh: bh of sec. + */ +static inline s32 __fat_copy(struct super_block *sb, u64 sec, struct buffer_head *bh, int sync) +{ +#ifdef CONFIG_SDFAT_FAT_MIRRORING + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u64 sec2; + + if (fsi->FAT2_start_sector != fsi->FAT1_start_sector) { + sec2 = sec - fsi->FAT1_start_sector + fsi->FAT2_start_sector; + BUG_ON(sec2 != (sec + (u64)fsi->num_FAT_sectors)); + + MMSG("BD: fat mirroring (%llu in FAT1, %llu in FAT2)\n", sec, sec2); + if (write_sect(sb, sec2, bh, sync)) + return -EIO; + } +#else + /* DO NOTHING */ +#endif + return 0; +} /* end of __fat_copy */ + +/* + * returns 1, if bp is flushed + * returns 0, if bp is not dirty + * returns -1, if error occurs + */ +static s32 __fcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync) +{ + if (!(bp->flag & DIRTYBIT)) + return 0; +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + // Make buffer dirty (XXX: Naive impl.) + if (write_sect(sb, bp->sec, bp->bh, 0)) + return -EIO; + + if (__fat_copy(sb, bp->sec, bp->bh, 0)) + return -EIO; +#endif + bp->flag &= ~(DIRTYBIT); + + if (sync) + sync_dirty_buffer(bp->bh); + + return 1; +} + +static s32 __fcache_ent_discard(struct super_block *sb, cache_ent_t *bp) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + __fcache_remove_hash(bp); + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + move_to_lru(bp, &fsi->fcache.lru_list); + return 0; +} + +u8 *fcache_getblk(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u32 page_ra_count = FCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits; + + bp = __fcache_find(sb, sec); + if (bp) { + if (bdev_check_bdi_valid(sb)) { + __fcache_ent_flush(sb, bp, 0); + __fcache_ent_discard(sb, bp); + return NULL; + } + move_to_mru(bp, &fsi->fcache.lru_list); + return bp->bh->b_data; + } + + bp = __fcache_get(sb); + if (!__check_hash_valid(bp)) + __fcache_remove_hash(bp); + + bp->sec = sec; + bp->flag = 0; + __fcache_insert_hash(sb, bp); + + /* Naive FAT read-ahead (increase I/O unit to page_ra_count) */ + if ((sec & (page_ra_count - 1)) == 0) + bdev_readahead(sb, sec, (u64)page_ra_count); + + /* + * patch 1.2.4 : buffer_head null pointer exception problem. + * + * When read_sect is failed, fcache should be moved to + * EMPTY hash_list and the first of lru_list. + */ + if (read_sect(sb, sec, &(bp->bh), 1)) { + __fcache_ent_discard(sb, bp); + return NULL; + } + + return bp->bh->b_data; +} + +static inline int __mark_delayed_dirty(struct super_block *sb, cache_ent_t *bp) +{ +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (fsi->vol_type == EXFAT) + return -ENOTSUPP; + + bp->flag |= DIRTYBIT; + return 0; +#else + return -ENOTSUPP; +#endif +} + + + +s32 fcache_modify(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + + bp = __fcache_find(sb, sec); + if (!bp) { + sdfat_fs_error(sb, "Can`t find fcache (sec 0x%016llx)", sec); + return -EIO; + } + + if (!__mark_delayed_dirty(sb, bp)) + return 0; + + if (write_sect(sb, sec, bp->bh, 0)) + return -EIO; + + if (__fat_copy(sb, sec, bp->bh, 0)) + return -EIO; + + return 0; +} + +/*======================================================================*/ +/* Cache Initialization Functions */ +/*======================================================================*/ +s32 meta_cache_init(struct super_block *sb) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 i; + + /* LRU list */ + fsi->fcache.lru_list.next = &fsi->fcache.lru_list; + fsi->fcache.lru_list.prev = fsi->fcache.lru_list.next; + + for (i = 0; i < FAT_CACHE_SIZE; i++) { + fsi->fcache.pool[i].sec = ~0; + fsi->fcache.pool[i].flag = 0; + fsi->fcache.pool[i].bh = NULL; + fsi->fcache.pool[i].prev = NULL; + fsi->fcache.pool[i].next = NULL; + push_to_mru(&(fsi->fcache.pool[i]), &fsi->fcache.lru_list); + } + + fsi->dcache.lru_list.next = &fsi->dcache.lru_list; + fsi->dcache.lru_list.prev = fsi->dcache.lru_list.next; + fsi->dcache.keep_list.next = &fsi->dcache.keep_list; + fsi->dcache.keep_list.prev = fsi->dcache.keep_list.next; + + // Initially, all the BUF_CACHEs are in the LRU list + for (i = 0; i < BUF_CACHE_SIZE; i++) { + fsi->dcache.pool[i].sec = ~0; + fsi->dcache.pool[i].flag = 0; + fsi->dcache.pool[i].bh = NULL; + fsi->dcache.pool[i].prev = NULL; + fsi->dcache.pool[i].next = NULL; + push_to_mru(&(fsi->dcache.pool[i]), &fsi->dcache.lru_list); + } + + /* HASH list */ + for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { + fsi->fcache.hash_list[i].sec = ~0; + fsi->fcache.hash_list[i].hash.next = &(fsi->fcache.hash_list[i]); +; + fsi->fcache.hash_list[i].hash.prev = fsi->fcache.hash_list[i].hash.next; + } + + for (i = 0; i < FAT_CACHE_SIZE; i++) + __fcache_insert_hash(sb, &(fsi->fcache.pool[i])); + + for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { + fsi->dcache.hash_list[i].sec = ~0; + fsi->dcache.hash_list[i].hash.next = &(fsi->dcache.hash_list[i]); + + fsi->dcache.hash_list[i].hash.prev = fsi->dcache.hash_list[i].hash.next; + } + + for (i = 0; i < BUF_CACHE_SIZE; i++) + __dcache_insert_hash(sb, &(fsi->dcache.pool[i])); + + return 0; +} + +s32 meta_cache_shutdown(struct super_block *sb) +{ + return 0; +} + +/*======================================================================*/ +/* FAT Read/Write Functions */ +/*======================================================================*/ +s32 fcache_release_all(struct super_block *sb) +{ + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 dirtycnt = 0; + + bp = fsi->fcache.lru_list.next; + while (bp != &fsi->fcache.lru_list) { + s32 ret_tmp = __fcache_ent_flush(sb, bp, 0); + + if (ret_tmp < 0) + ret = ret_tmp; + else + dirtycnt += ret_tmp; + + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + bp = bp->next; + } + + DMSG("BD:Release / dirty fat cache: %d (err:%d)\n", dirtycnt, ret); + return ret; +} + + +/* internal DIRTYBIT marked => bh dirty */ +s32 fcache_flush(struct super_block *sb, u32 sync) +{ + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 dirtycnt = 0; + + bp = fsi->fcache.lru_list.next; + while (bp != &fsi->fcache.lru_list) { + ret = __fcache_ent_flush(sb, bp, sync); + if (ret < 0) + break; + + dirtycnt += ret; + bp = bp->next; + } + + MMSG("BD: flush / dirty fat cache: %d (err:%d)\n", dirtycnt, ret); + return ret; +} + +static cache_ent_t *__fcache_find(struct super_block *sb, u64 sec) +{ + s32 off; + cache_ent_t *bp, *hp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + off = (sec + (sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE - 1); + hp = &(fsi->fcache.hash_list[off]); + for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) { + if (bp->sec == sec) { + /* + * patch 1.2.4 : for debugging + */ + WARN(!bp->bh, "[SDFAT] fcache has no bh. " + "It will make system panic.\n"); + + touch_buffer(bp->bh); + return bp; + } + } + return NULL; +} + +static cache_ent_t *__fcache_get(struct super_block *sb) +{ + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + bp = fsi->fcache.lru_list.prev; +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + while (bp->flag & DIRTYBIT) { + cache_ent_t *bp_prev = bp->prev; + + bp = bp_prev; + if (bp == &fsi->fcache.lru_list) { + DMSG("BD: fat cache flooding\n"); + fcache_flush(sb, 0); // flush all dirty FAT caches + bp = fsi->fcache.lru_list.prev; + } + } +#endif +// if (bp->flag & DIRTYBIT) +// sync_dirty_buffer(bp->bh); + + move_to_mru(bp, &fsi->fcache.lru_list); + return bp; +} + +static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp) +{ + s32 off; + cache_ent_t *hp; + FS_INFO_T *fsi; + + fsi = &(SDFAT_SB(sb)->fsi); + off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE-1); + + hp = &(fsi->fcache.hash_list[off]); + bp->hash.next = hp->hash.next; + bp->hash.prev = hp; + hp->hash.next->hash.prev = bp; + hp->hash.next = bp; +} + + +static void __fcache_remove_hash(cache_ent_t *bp) +{ +#ifdef DEBUG_HASH_LIST + if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) || + (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) { + EMSG("%s: FATAL: tried to remove already-removed-cache-entry" + "(bp:%p)\n", __func__, bp); + return; + } +#endif + WARN_ON(bp->flag & DIRTYBIT); + __remove_from_hash(bp); +} + +/*======================================================================*/ +/* Buffer Read/Write Functions */ +/*======================================================================*/ +/* Read-ahead a cluster */ +s32 dcache_readahead(struct super_block *sb, u64 sec) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + struct buffer_head *bh; + u32 max_ra_count = DCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits; + u32 page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits; + u32 adj_ra_count = max(fsi->sect_per_clus, page_ra_count); + u32 ra_count = min(adj_ra_count, max_ra_count); + + /* Read-ahead is not required */ + if (fsi->sect_per_clus == 1) + return 0; + + if (sec < fsi->data_start_sector) { + EMSG("BD: %s: requested sector is invalid(sect:%llu, root:%llu)\n", + __func__, sec, fsi->data_start_sector); + return -EIO; + } + + /* Not sector aligned with ra_count, resize ra_count to page size */ + if ((sec - fsi->data_start_sector) & (ra_count - 1)) + ra_count = page_ra_count; + + bh = sb_find_get_block(sb, sec); + if (!bh || !buffer_uptodate(bh)) + bdev_readahead(sb, sec, (u64)ra_count); + + brelse(bh); + + return 0; +} + +/* + * returns 1, if bp is flushed + * returns 0, if bp is not dirty + * returns -1, if error occurs + */ +static s32 __dcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync) +{ + if (!(bp->flag & DIRTYBIT)) + return 0; +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + // Make buffer dirty (XXX: Naive impl.) + if (write_sect(sb, bp->sec, bp->bh, 0)) + return -EIO; +#endif + bp->flag &= ~(DIRTYBIT); + + if (sync) + sync_dirty_buffer(bp->bh); + + return 1; +} + +static s32 __dcache_ent_discard(struct super_block *sb, cache_ent_t *bp) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + MMSG("%s : bp[%p] (sec:%016llx flag:%08x bh:%p) list(prev:%p next:%p) " + "hash(prev:%p next:%p)\n", __func__, + bp, bp->sec, bp->flag, bp->bh, bp->prev, bp->next, + bp->hash.prev, bp->hash.next); + + __dcache_remove_hash(bp); + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + + move_to_lru(bp, &fsi->dcache.lru_list); + return 0; +} + +u8 *dcache_getblk(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + bp = __dcache_find(sb, sec); + if (bp) { + if (bdev_check_bdi_valid(sb)) { + MMSG("%s: found cache(%p, sect:%llu). But invalid BDI\n" + , __func__, bp, sec); + __dcache_ent_flush(sb, bp, 0); + __dcache_ent_discard(sb, bp); + return NULL; + } + + if (!(bp->flag & KEEPBIT)) // already in keep list + move_to_mru(bp, &fsi->dcache.lru_list); + + return bp->bh->b_data; + } + + bp = __dcache_get(sb); + + if (!__check_hash_valid(bp)) + __dcache_remove_hash(bp); + + bp->sec = sec; + bp->flag = 0; + __dcache_insert_hash(sb, bp); + + if (read_sect(sb, sec, &(bp->bh), 1)) { + __dcache_ent_discard(sb, bp); + return NULL; + } + + return bp->bh->b_data; + +} + +s32 dcache_modify(struct super_block *sb, u64 sec) +{ + s32 ret = -EIO; + cache_ent_t *bp; + + set_sb_dirty(sb); + + bp = __dcache_find(sb, sec); + if (unlikely(!bp)) { + sdfat_fs_error(sb, "Can`t find dcache (sec 0x%016llx)", sec); + return -EIO; + } +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + if (SDFAT_SB(sb)->fsi.vol_type != EXFAT) { + bp->flag |= DIRTYBIT; + return 0; + } +#endif + ret = write_sect(sb, sec, bp->bh, 0); + + if (ret) { + DMSG("%s : failed to modify buffer(err:%d, sec:%llu, bp:0x%p)\n", + __func__, ret, sec, bp); + } + + return ret; +} + +s32 dcache_lock(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + + bp = __dcache_find(sb, sec); + if (likely(bp)) { + bp->flag |= LOCKBIT; + return 0; + } + + EMSG("%s : failed to lock buffer(sec:%llu, bp:0x%p)\n", __func__, sec, bp); + return -EIO; +} + +s32 dcache_unlock(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + + bp = __dcache_find(sb, sec); + if (likely(bp)) { + bp->flag &= ~(LOCKBIT); + return 0; + } + + EMSG("%s : failed to unlock buffer (sec:%llu, bp:0x%p)\n", __func__, sec, bp); + return -EIO; +} + +s32 dcache_release(struct super_block *sb, u64 sec) +{ + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + bp = __dcache_find(sb, sec); + if (unlikely(!bp)) + return -ENOENT; + +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + if (bp->flag & DIRTYBIT) { + if (write_sect(sb, bp->sec, bp->bh, 0)) + return -EIO; + } +#endif + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + + move_to_lru(bp, &fsi->dcache.lru_list); + return 0; +} + +s32 dcache_release_all(struct super_block *sb) +{ + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 dirtycnt = 0; + + /* Connect list elements: + * LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last) + */ + while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) { + cache_ent_t *bp_keep = fsi->dcache.keep_list.prev; + // bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later + move_to_mru(bp_keep, &fsi->dcache.lru_list); + } + + bp = fsi->dcache.lru_list.next; + while (bp != &fsi->dcache.lru_list) { +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + if (bp->flag & DIRTYBIT) { + dirtycnt++; + if (write_sect(sb, bp->sec, bp->bh, 0)) + ret = -EIO; + } +#endif + bp->sec = ~0; + bp->flag = 0; + + if (bp->bh) { + __brelse(bp->bh); + bp->bh = NULL; + } + bp = bp->next; + } + + DMSG("BD:Release / dirty buf cache: %d (err:%d)", dirtycnt, ret); + return ret; +} + + +s32 dcache_flush(struct super_block *sb, u32 sync) +{ + s32 ret = 0; + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 dirtycnt = 0; + s32 keepcnt = 0; + + /* Connect list elements: + * LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last) + */ + while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) { + cache_ent_t *bp_keep = fsi->dcache.keep_list.prev; + + bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later + move_to_mru(bp_keep, &fsi->dcache.lru_list); + keepcnt++; + } + + bp = fsi->dcache.lru_list.next; + while (bp != &fsi->dcache.lru_list) { + if (bp->flag & DIRTYBIT) { +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + // Make buffer dirty (XXX: Naive impl.) + if (write_sect(sb, bp->sec, bp->bh, 0)) { + ret = -EIO; + break; + } + +#endif + bp->flag &= ~(DIRTYBIT); + dirtycnt++; + + if (sync != 0) + sync_dirty_buffer(bp->bh); + } + bp = bp->next; + } + + MMSG("BD: flush / dirty dentry cache: %d (%d from keeplist, err:%d)\n", + dirtycnt, keepcnt, ret); + return ret; +} + +static cache_ent_t *__dcache_find(struct super_block *sb, u64 sec) +{ + s32 off; + cache_ent_t *bp, *hp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + off = (sec + (sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE - 1); + + hp = &(fsi->dcache.hash_list[off]); + for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) { + if (bp->sec == sec) { + touch_buffer(bp->bh); + return bp; + } + } + return NULL; +} + +static cache_ent_t *__dcache_get(struct super_block *sb) +{ + cache_ent_t *bp; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + bp = fsi->dcache.lru_list.prev; +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + while (bp->flag & (DIRTYBIT | LOCKBIT)) { + cache_ent_t *bp_prev = bp->prev; // hold prev + + if (bp->flag & DIRTYBIT) { + MMSG("BD: Buf cache => Keep list\n"); + bp->flag |= KEEPBIT; + move_to_mru(bp, &fsi->dcache.keep_list); + } + bp = bp_prev; + + /* If all dcaches are dirty */ + if (bp == &fsi->dcache.lru_list) { + DMSG("BD: buf cache flooding\n"); + dcache_flush(sb, 0); + bp = fsi->dcache.lru_list.prev; + } + } +#else + while (bp->flag & LOCKBIT) + bp = bp->prev; +#endif +// if (bp->flag & DIRTYBIT) +// sync_dirty_buffer(bp->bh); + + move_to_mru(bp, &fsi->dcache.lru_list); + return bp; +} + +static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp) +{ + s32 off; + cache_ent_t *hp; + FS_INFO_T *fsi; + + fsi = &(SDFAT_SB(sb)->fsi); + off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE-1); + + hp = &(fsi->dcache.hash_list[off]); + bp->hash.next = hp->hash.next; + bp->hash.prev = hp; + hp->hash.next->hash.prev = bp; + hp->hash.next = bp; +} + +static void __dcache_remove_hash(cache_ent_t *bp) +{ +#ifdef DEBUG_HASH_LIST + if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) || + (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) { + EMSG("%s: FATAL: tried to remove already-removed-cache-entry" + "(bp:%p)\n", __func__, bp); + return; + } +#endif + WARN_ON(bp->flag & DIRTYBIT); + __remove_from_hash(bp); +} + + +/* end of cache.c */ diff --git a/fs/sdfat/config.h b/fs/sdfat/config.h new file mode 100644 index 0000000000000..6e2a4e80932c6 --- /dev/null +++ b/fs/sdfat/config.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _SDFAT_CONFIG_H +#define _SDFAT_CONFIG_H +/*======================================================================*/ +/* */ +/* FFS CONFIGURATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* Feature Config */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Debug/Experimental Config */ +/*----------------------------------------------------------------------*/ +//#define CONFIG_SDFAT_TRACE_IO +//#define CONFIG_SDFAT_TRACE_LOCK /* Trace elapsed time in lock_super(sb) */ + +/*----------------------------------------------------------------------*/ +/* Defragmentation Config */ +/*----------------------------------------------------------------------*/ +//#define CONFIG_SDFAT_DFR +//#define CONFIG_SDFAT_DFR_PACKING +//#define CONFIG_SDFAT_DFR_DEBUG + +/*----------------------------------------------------------------------*/ +/* Config for Kernel equal or newer than 3.7 */ +/*----------------------------------------------------------------------*/ +#ifndef CONFIG_SDFAT_WRITE_SB_INTERVAL_CSECS +#define CONFIG_SDFAT_WRITE_SB_INTERVAL_CSECS (dirty_writeback_interval) +#endif + +/*----------------------------------------------------------------------*/ +/* Default Kconfig */ +/*----------------------------------------------------------------------*/ +/* default mount options */ +#ifndef CONFIG_SDFAT_DEFAULT_CODEPAGE /* if Kconfig lacked codepage */ +#define CONFIG_SDFAT_DEFAULT_CODEPAGE 437 +#endif + +#ifndef CONFIG_SDFAT_DEFAULT_IOCHARSET /* if Kconfig lacked iocharset */ +#define CONFIG_SDFAT_DEFAULT_IOCHARSET "utf8" +#endif + +#ifndef CONFIG_SDFAT_FAT32_SHORTNAME_SEQ /* Shortname ~1, ... ~9 have higher + * priority (WIN32/VFAT-like) + */ +//#define CONFIG_SDFAT_FAT32_SHORTNAME_SEQ +#endif + +#ifndef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE +//#define CONFIG_SDFAT_ALIGNED_MPAGE_WRITE +#endif + +#ifndef CONFIG_SDFAT_FAT_MIRRORING /* if Kconfig lacked fat-mirroring option */ +#define CONFIG_SDFAT_FAT_MIRRORING /* Write FAT 1, FAT 2 simultaneously */ +#endif + +#ifndef CONFIG_SDFAT_DELAYED_META_DIRTY +//#define CONFIG_SDFAT_DELAYED_META_DIRTY /* delayed DIR/FAT dirty support */ +#endif + +#ifndef CONFIG_SDFAT_SUPPORT_DIR_SYNC +//#define CONFIG_SDFAT_SUPPORT_DIR_SYNC /* support DIR_SYNC */ +#endif + +#ifndef CONFIG_SDFAT_CHECK_RO_ATTR +//#define CONFIG_SDFAT_CHECK_RO_ATTR +#endif + +#ifndef CONFIG_SDFAT_RESTRICT_EXT_ONLY_SFN +#define CONFIG_SDFAT_RESTRICT_EXT_ONLY_SFN +#endif + +#ifndef CONFIG_SDFAT_ALLOW_LOOKUP_LOSSY_SFN +//#define CONFIG_SDFAT_ALLOW_LOOKUP_LOSSY_SFN +#endif + +#ifndef CONFIG_SDFAT_DBG_SHOW_PID +//#define CONFIG_SDFAT_DBG_SHOW_PID +#endif + +#ifndef CONFIG_SDFAT_VIRTUAL_XATTR +//#define CONFIG_SDFAT_VIRTUAL_XATTR +#endif + +#ifndef CONFIG_SDFAT_SUPPORT_STLOG +//#define CONFIG_SDFAT_SUPPORT_STLOG +#endif + +#ifndef CONFIG_SDFAT_DEBUG +//{ +//#define CONFIG_SDFAT_DEBUG + +#ifndef CONFIG_SDFAT_DBG_IOCTL +//#define CONFIG_SDFAT_DBG_IOCTL +#endif + +#ifndef CONFIG_SDFAT_DBG_MSG +//#define CONFIG_SDFAT_DBG_MSG +#endif + +#ifndef CONFIG_SDFAT_DBG_CAREFUL +//#define CONFIG_SDFAT_DBG_CAREFUL +#endif + +#ifndef CONFIG_SDFAT_DBG_BUGON +//#define CONFIG_SDFAT_DBG_BUGON +#endif + +#ifndef CONFIG_SDFAT_DBG_WARNON +//#define CONFIG_SDFAT_DBG_WARNON +#endif +//} +#endif /* CONFIG_SDFAT_DEBUG */ + + +#ifndef CONFIG_SDFAT_TRACE_SB_LOCK +//#define CONFIG_SDFAT_TRACE_SB_LOCK +#endif + +#ifndef CONFIG_SDFAT_TRACE_ELAPSED_TIME +//#define CONFIG_SDFAT_TRACE_ELAPSED_TIME +#endif + +#endif /* _SDFAT_CONFIG_H */ + +/* end of config.h */ diff --git a/fs/sdfat/core.c b/fs/sdfat/core.c new file mode 100644 index 0000000000000..357eb0d54f1c9 --- /dev/null +++ b/fs/sdfat/core.c @@ -0,0 +1,3666 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : core.c */ +/* PURPOSE : FAT & exFAT common core code for sdFAT */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "sdfat.h" +#include "core.h" +#include +#include + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ +static inline void __set_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + sb->s_dirt = 1; +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) */ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + sbi->s_dirt = 1; + /* Insert work */ + spin_lock(&sbi->work_lock); + if (!sbi->write_super_queued) { + unsigned long delay; + + delay = msecs_to_jiffies(CONFIG_SDFAT_WRITE_SB_INTERVAL_CSECS * 10); + queue_delayed_work(system_long_wq, &sbi->write_super_work, delay); + sbi->write_super_queued = 1; + } + spin_unlock(&sbi->work_lock); +#endif +} + +void set_sb_dirty(struct super_block *sb) +{ + __set_sb_dirty(sb); + // XXX: to be removed later, prints too much output + //TMSG("%s finished.\n", __func__); +} + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static s8 *reserved_names[] = { + "AUX ", "CON ", "NUL ", "PRN ", + "COM1 ", "COM2 ", "COM3 ", "COM4 ", + "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ", + "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", + "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ", + NULL +}; + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +/* + * File System Management Functions + */ + +static s32 check_type_size(void) +{ + /* critical check for system requirement on size of DENTRY_T structure */ + if (sizeof(DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) + return -EINVAL; + + return 0; +} + +static s32 __fs_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 err; + s32 sync = 0; + + /* flags are not changed */ + if (fsi->vol_flag == new_flag) + return 0; + + fsi->vol_flag = new_flag; + + /* skip updating volume dirty flag, + * if this volume has been mounted with read-only + */ + if (sb->s_flags & MS_RDONLY) + return 0; + + if (!fsi->pbr_bh) { + err = read_sect(sb, 0, &(fsi->pbr_bh), 1); + if (err) { + EMSG("%s : failed to read boot sector\n", __func__); + return err; + } + } + + if (fsi->vol_type == EXFAT) { + pbr64_t *bpb = (pbr64_t *)fsi->pbr_bh->b_data; + bpb->bsx.vol_flags = cpu_to_le16(new_flag); + } else if (fsi->vol_type == FAT32) { + pbr32_t *bpb = (pbr32_t *)fsi->pbr_bh->b_data; + bpb->bsx.state = new_flag & VOL_DIRTY ? FAT_VOL_DIRTY : 0x00; + } else { /* FAT16/12 */ + pbr16_t *bpb = (pbr16_t *) fsi->pbr_bh->b_data; + bpb->bpb.state = new_flag & VOL_DIRTY ? FAT_VOL_DIRTY : 0x00; + } + + if (always_sync) + sync = 1; + else if ((new_flag == VOL_DIRTY) && (!buffer_dirty(fsi->pbr_bh))) + sync = 1; + else + sync = 0; + + err = write_sect(sb, 0, fsi->pbr_bh, sync); + if (err) + EMSG("%s : failed to modify volume flag\n", __func__); + + return err; +} + +static s32 fs_set_vol_flags(struct super_block *sb, u16 new_flag) +{ + return __fs_set_vol_flags(sb, new_flag, 0); +} + +s32 fscore_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync) +{ + return __fs_set_vol_flags(sb, new_flag, always_sync); +} + +static inline s32 __fs_meta_sync(struct super_block *sb, s32 do_sync) +{ +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (fsi->vol_type != EXFAT) { + MMSG("meta flush in fs_sync(sync=%d)\n", do_sync); + fcache_flush(sb, 0); + dcache_flush(sb, 0); + } +#else + /* DO NOTHING */ +#endif + return 0; +} + +static s32 fs_sync(struct super_block *sb, s32 do_sync) +{ + s32 err; + + if (!do_sync) + return 0; + + err = __fs_meta_sync(sb, do_sync); + + if (!err) + err = bdev_sync_all(sb); + + if (err) + EMSG("%s : failed to sync. (err:%d)\n", __func__, err); + + return err; +} + +/* + * Cluster Management Functions + */ + +static s32 __clear_cluster(struct inode *inode, u32 clu) +{ + u64 s, n; + struct super_block *sb = inode->i_sb; + u32 sect_size = (u32)sb->s_blocksize; + s32 ret = 0; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(clu)) { /* FAT16 root_dir */ + s = fsi->root_start_sector; + n = fsi->data_start_sector; + } else { + s = CLUS_TO_SECT(fsi, clu); + n = s + fsi->sect_per_clus; + } + + if (IS_DIRSYNC(inode)) { + ret = write_msect_zero(sb, s, (u64)fsi->sect_per_clus); + if (ret != -EAGAIN) + return ret; + } + + /* Trying buffered zero writes + * if it doesn't have DIRSYNC or write_msect_zero() returned -EAGAIN + */ + for ( ; s < n; s++) { +#if 0 + dcache_release(sb, s); +#endif + ret = read_sect(sb, s, &tmp_bh, 0); + if (ret) + goto out; + + memset((u8 *)tmp_bh->b_data, 0x0, sect_size); + ret = write_sect(sb, s, tmp_bh, 0); + if (ret) + goto out; + } +out: + brelse(tmp_bh); + return ret; +} /* end of __clear_cluster */ + +static s32 __find_last_cluster(struct super_block *sb, CHAIN_T *p_chain, u32 *ret_clu) +{ + u32 clu, next; + u32 count = 0; + + next = p_chain->dir; + if (p_chain->flags == 0x03) { + *ret_clu = next + p_chain->size - 1; + return 0; + } + + do { + count++; + clu = next; + if (fat_ent_get_safe(sb, clu, &next)) + return -EIO; + } while (!IS_CLUS_EOF(next)); + + if (p_chain->size != count) { + sdfat_fs_error(sb, "bogus directory size " + "(clus : ondisk(%d) != counted(%d))", + p_chain->size, count); + sdfat_debug_bug_on(1); + return -EIO; + } + + *ret_clu = clu; + return 0; +} + + +static s32 __count_num_clusters(struct super_block *sb, CHAIN_T *p_chain, u32 *ret_count) +{ + u32 i, count; + u32 clu; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (!p_chain->dir || IS_CLUS_EOF(p_chain->dir)) { + *ret_count = 0; + return 0; + } + + if (p_chain->flags == 0x03) { + *ret_count = p_chain->size; + return 0; + } + + clu = p_chain->dir; + count = 0; + for (i = CLUS_BASE; i < fsi->num_clusters; i++) { + count++; + if (fat_ent_get_safe(sb, clu, &clu)) + return -EIO; + if (IS_CLUS_EOF(clu)) + break; + } + + *ret_count = count; + return 0; +} + +/* + * Upcase table Management Functions + */ +static void free_upcase_table(struct super_block *sb) +{ + u32 i; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u16 **upcase_table; + + upcase_table = fsi->vol_utbl; + for (i = 0 ; i < UTBL_COL_COUNT ; i++) { + /* kfree(NULL) is safe */ + kfree(upcase_table[i]); + upcase_table[i] = NULL; + } + + /* kfree(NULL) is safe */ + kfree(fsi->vol_utbl); + fsi->vol_utbl = NULL; +} + +static s32 __load_upcase_table(struct super_block *sb, u64 sector, u64 num_sectors, u32 utbl_checksum) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + struct buffer_head *tmp_bh = NULL; + u32 sect_size = (u32)sb->s_blocksize; + s32 ret = -EIO; + u32 i, j; + + u8 skip = false; + u32 index = 0; + u32 checksum = 0; + u16 **upcase_table = kzalloc((UTBL_COL_COUNT * sizeof(u16 *)), GFP_KERNEL); + + if (!upcase_table) + return -ENOMEM; + /* thanks for kzalloc + * memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + */ + + fsi->vol_utbl = upcase_table; + num_sectors += sector; + + while (sector < num_sectors) { + ret = read_sect(sb, sector, &tmp_bh, 1); + if (ret) { + EMSG("%s: failed to read sector(0x%llx)\n", + __func__, sector); + goto error; + } + sector++; + + for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) { + /* FIXME : is __le16 ok? */ + //u16 uni = le16_to_cpu(((__le16*)(tmp_bh->b_data))[i]); + u16 uni = get_unaligned_le16((u8 *)tmp_bh->b_data+i); + + checksum = ((checksum & 1) ? 0x80000000 : 0) + + (checksum >> 1) + *(((u8 *)tmp_bh->b_data)+i); + checksum = ((checksum & 1) ? 0x80000000 : 0) + + (checksum >> 1) + *(((u8 *)tmp_bh->b_data)+(i+1)); + + if (skip) { + MMSG("skip from 0x%X to 0x%X(amount of 0x%X)\n", + index, index+uni, uni); + index += uni; + skip = false; + } else if (uni == index) { + index++; + } else if (uni == 0xFFFF) { + skip = true; + } else { /* uni != index , uni != 0xFFFF */ + u16 col_index = get_col_index(index); + + if (!upcase_table[col_index]) { + upcase_table[col_index] = + kmalloc((UTBL_ROW_COUNT * sizeof(u16)), GFP_KERNEL); + if (!upcase_table[col_index]) { + EMSG("failed to allocate memory" + " for column 0x%X\n", + col_index); + ret = -ENOMEM; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index++; + } + } + } + if (index >= 0xFFFF && utbl_checksum == checksum) { + DMSG("%s: load upcase table successfully" + "(idx:0x%08x, utbl_chksum:0x%08x)\n", + __func__, index, utbl_checksum); + if (tmp_bh) + brelse(tmp_bh); + return 0; + } + + EMSG("%s: failed to load upcase table" + "(idx:0x%08x, chksum:0x%08x, utbl_chksum:0x%08x)\n", + __func__, index, checksum, utbl_checksum); + + ret = -EINVAL; +error: + if (tmp_bh) + brelse(tmp_bh); + free_upcase_table(sb); + return ret; +} + +static s32 __load_default_upcase_table(struct super_block *sb) +{ + s32 i, ret = -EIO; + u32 j; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + u8 skip = false; + u32 index = 0; + u16 uni = 0; + u16 **upcase_table; + + upcase_table = kmalloc((UTBL_COL_COUNT * sizeof(u16 *)), GFP_KERNEL); + if (!upcase_table) + return -ENOMEM; + + fsi->vol_utbl = upcase_table; + memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + + for (i = 0; index <= 0xFFFF && i < SDFAT_NUM_UPCASE*2; i += 2) { + /* FIXME : is __le16 ok? */ + //uni = le16_to_cpu(((__le16*)uni_def_upcase)[i>>1]); + uni = get_unaligned_le16((u8 *)uni_def_upcase+i); + if (skip) { + MMSG("skip from 0x%x ", index); + index += uni; + MMSG("to 0x%x (amount of 0x%x)\n", index, uni); + skip = false; + } else if (uni == index) { + index++; + } else if (uni == 0xFFFF) { + skip = true; + } else { /* uni != index , uni != 0xFFFF */ + u16 col_index = get_col_index(index); + + if (!upcase_table[col_index]) { + upcase_table[col_index] = kmalloc((UTBL_ROW_COUNT * sizeof(u16)), GFP_KERNEL); + if (!upcase_table[col_index]) { + EMSG("failed to allocate memory for " + "new column 0x%x\n", col_index); + ret = -ENOMEM; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index++; + } + } + + if (index >= 0xFFFF) + return 0; + +error: + /* FATAL error: default upcase table has error */ + free_upcase_table(sb); + return ret; +} + +static s32 load_upcase_table(struct super_block *sb) +{ + s32 i, ret; + u32 tbl_clu, type; + u64 sector, tbl_size, num_sectors; + u8 blksize_bits = sb->s_blocksize_bits; + CHAIN_T clu; + CASE_DENTRY_T *ep; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + clu.dir = fsi->root_dir; + clu.flags = 0x01; + + if (fsi->vol_type != EXFAT) + goto load_default; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < fsi->dentries_per_clu; i++) { + ep = (CASE_DENTRY_T *) get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_UPCASE) + continue; + + tbl_clu = le32_to_cpu(ep->start_clu); + tbl_size = le64_to_cpu(ep->size); + + sector = CLUS_TO_SECT(fsi, tbl_clu); + num_sectors = ((tbl_size - 1) >> blksize_bits) + 1; + ret = __load_upcase_table(sb, sector, num_sectors, + le32_to_cpu(ep->checksum)); + + if (ret && (ret != -EIO)) + goto load_default; + + /* load successfully */ + return ret; + } + + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + +load_default: + sdfat_log_msg(sb, KERN_INFO, "trying to load default upcase table"); + /* load default upcase table */ + return __load_default_upcase_table(sb); +} /* end of load_upcase_table */ + + +/* + * Directory Entry Management Functions + */ +s32 walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, u32 byte_offset, u32 *clu) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u32 clu_offset; + u32 cur_clu; + + clu_offset = byte_offset >> fsi->cluster_size_bits; + cur_clu = p_dir->dir; + + if (p_dir->flags == 0x03) { + cur_clu += clu_offset; + } else { + while (clu_offset > 0) { + if (get_next_clus_safe(sb, &cur_clu)) + return -EIO; + if (IS_CLUS_EOF(cur_clu)) { + sdfat_fs_error(sb, "invalid dentry access " + "beyond EOF (clu : %u, eidx : %d)", + p_dir->dir, + byte_offset >> DENTRY_SIZE_BITS); + return -EIO; + } + clu_offset--; + } + } + + if (clu) + *clu = cur_clu; + return 0; +} + +static s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector, s32 *offset) +{ + s32 ret; + u32 off, clu = 0; + u32 blksize_mask = (u32)(sb->s_blocksize-1); + u8 blksize_bits = sb->s_blocksize_bits; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + off = entry << DENTRY_SIZE_BITS; + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) { + *offset = off & blksize_mask; + *sector = off >> blksize_bits; + *sector += fsi->root_start_sector; + return 0; + } + + ret = walk_fat_chain(sb, p_dir, off, &clu); + if (ret) + return ret; + + /* byte offset in cluster */ + off &= (fsi->cluster_size - 1); + + /* byte offset in sector */ + *offset = off & blksize_mask; + + /* sector offset in cluster */ + *sector = off >> blksize_bits; + *sector += CLUS_TO_SECT(fsi, clu); + return 0; +} /* end of find_location */ + +DENTRY_T *get_dentry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u32 dentries_per_page = PAGE_SIZE >> DENTRY_SIZE_BITS; + s32 off; + u64 sec; + u8 *buf; + + if (p_dir->dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + BUG_ON(!fsi->prev_eio); + return NULL; + } + + if (find_location(sb, p_dir, entry, &sec, &off)) + return NULL; + + /* DIRECTORY READAHEAD : + * Try to read ahead per a page except root directory of fat12/16 + */ + if ((!IS_CLUS_FREE(p_dir->dir)) && + !(entry & (dentries_per_page - 1))) + dcache_readahead(sb, sec); + + buf = dcache_getblk(sb, sec); + if (!buf) + return NULL; + + if (sector) + *sector = sec; + return (DENTRY_T *)(buf + off); +} /* end of get_dentry_in_dir */ + +/* used only in search empty_slot() */ +#define CNT_UNUSED_NOHIT (-1) +#define CNT_UNUSED_HIT (-2) +/* search EMPTY CONTINUOUS "num_entries" entries */ +static s32 search_empty_slot(struct super_block *sb, HINT_FEMP_T *hint_femp, CHAIN_T *p_dir, s32 num_entries) +{ + s32 i, dentry, num_empty = 0; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + else + dentries_per_clu = fsi->dentries_per_clu; + + ASSERT(-1 <= hint_femp->eidx); + + if (hint_femp->eidx != -1) { + clu.dir = hint_femp->cur.dir; + clu.size = hint_femp->cur.size; + clu.flags = hint_femp->cur.flags; + + dentry = hint_femp->eidx; + + if (num_entries <= hint_femp->count) { + MMSG("%s: empty slot(HIT) - found " + "(clu : 0x%08x eidx : %d)\n", + __func__, hint_femp->cur.dir, hint_femp->eidx); + hint_femp->eidx = -1; + + if (fsi->vol_type == EXFAT) + return dentry; + + return dentry + (num_entries - 1); + } + MMSG("%s: empty slot(HIT) - search from " + "(clu : 0x%08x eidx : %d)\n", + __func__, hint_femp->cur.dir, hint_femp->eidx); + } else { + MMSG("%s: empty slot(MISS) - search from " + "(clu:0x%08x eidx : 0)\n", + __func__, p_dir->dir); + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + dentry = 0; + } + + while (!IS_CLUS_EOF(clu.dir)) { + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type(ep); + + if ((type == TYPE_UNUSED) || (type == TYPE_DELETED)) { + num_empty++; + if (hint_femp->eidx == -1) { + hint_femp->eidx = dentry; + hint_femp->count = CNT_UNUSED_NOHIT; + + hint_femp->cur.dir = clu.dir; + hint_femp->cur.size = clu.size; + hint_femp->cur.flags = clu.flags; + } + + if ((type == TYPE_UNUSED) && + (hint_femp->count != CNT_UNUSED_HIT)) { + hint_femp->count = CNT_UNUSED_HIT; + } + } else { + if ((hint_femp->eidx != -1) && + (hint_femp->count == CNT_UNUSED_HIT)) { + /* unused empty group means + * an empty group which includes + * unused dentry + */ + sdfat_fs_error(sb, + "found bogus dentry(%d) " + "beyond unused empty group(%d) " + "(start_clu : %u, cur_clu : %u)", + dentry, hint_femp->eidx, p_dir->dir, + clu.dir); + return -EIO; + } + + num_empty = 0; + hint_femp->eidx = -1; + } + + if (num_empty >= num_entries) { + /* found and invalidate hint_femp */ + hint_femp->eidx = -1; + + if (fsi->vol_type == EXFAT) + return (dentry - (num_entries-1)); + + return dentry; + } + } + + if (IS_CLUS_FREE(p_dir->dir)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + } + + return -ENOSPC; +} /* end of search_empty_slot */ + +/* find empty directory entry. + * if there isn't any empty slot, expand cluster chain. + */ +static s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries) +{ + s32 dentry; + u32 ret, last_clu; + u64 sector; + u64 size = 0; + CHAIN_T clu; + DENTRY_T *ep = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + HINT_FEMP_T hint_femp; + + hint_femp.eidx = -1; + + ASSERT(-1 <= fid->hint_femp.eidx); + + if (fid->hint_femp.eidx != -1) { + memcpy(&hint_femp, &fid->hint_femp, sizeof(HINT_FEMP_T)); + fid->hint_femp.eidx = -1; + } + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + return search_empty_slot(sb, &hint_femp, p_dir, num_entries); + + while ((dentry = search_empty_slot(sb, &hint_femp, p_dir, num_entries)) < 0) { + if (dentry == -EIO) + break; + + if (fsi->fs_func->check_max_dentries(fid)) + return -ENOSPC; + + /* we trust p_dir->size regardless of FAT type */ + if (__find_last_cluster(sb, p_dir, &last_clu)) + return -EIO; + + /* + * Allocate new cluster to this directory + */ + clu.dir = last_clu + 1; + clu.size = 0; /* UNUSED */ + clu.flags = p_dir->flags; + + /* (0) check if there are reserved clusters + * (create_dir 의 주석 참고) + */ + if (!IS_CLUS_EOF(fsi->used_clusters) && + ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - 2))) + return -ENOSPC; + + /* (1) allocate a cluster */ + ret = fsi->fs_func->alloc_cluster(sb, 1, &clu, ALLOC_HOT); + if (ret) + return ret; + + if (__clear_cluster(inode, clu.dir)) + return -EIO; + + /* (2) append to the FAT chain */ + if (clu.flags != p_dir->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with alloc-bmp + */ + chain_cont_cluster(sb, p_dir->dir, p_dir->size); + p_dir->flags = 0x01; + hint_femp.cur.flags = 0x01; + } + + if (clu.flags == 0x01) + if (fat_ent_set(sb, last_clu, clu.dir)) + return -EIO; + + if (hint_femp.eidx == -1) { + /* the special case that new dentry + * should be allocated from the start of new cluster + */ + hint_femp.eidx = (s32)(p_dir->size << + (fsi->cluster_size_bits - DENTRY_SIZE_BITS)); + hint_femp.count = fsi->dentries_per_clu; + + hint_femp.cur.dir = clu.dir; + hint_femp.cur.size = 0; + hint_femp.cur.flags = clu.flags; + } + hint_femp.cur.size++; + p_dir->size++; + size = (p_dir->size << fsi->cluster_size_bits); + + /* (3) update the directory entry */ + if ((fsi->vol_type == EXFAT) && (p_dir->dir != fsi->root_dir)) { + ep = get_dentry_in_dir(sb, + &(fid->dir), fid->entry+1, §or); + if (!ep) + return -EIO; + fsi->fs_func->set_entry_size(ep, size); + fsi->fs_func->set_entry_flag(ep, p_dir->flags); + if (dcache_modify(sb, sector)) + return -EIO; + + if (update_dir_chksum(sb, &(fid->dir), fid->entry)) + return -EIO; + } + + /* directory inode should be updated in here */ + i_size_write(inode, (loff_t)size); + SDFAT_I(inode)->i_size_ondisk += fsi->cluster_size; + SDFAT_I(inode)->i_size_aligned += fsi->cluster_size; + SDFAT_I(inode)->fid.size = size; + SDFAT_I(inode)->fid.flags = p_dir->flags; + inode->i_blocks += 1 << (fsi->cluster_size_bits - sb->s_blocksize_bits); + } + + return dentry; +} /* end of find_empty_entry */ + +#define SDFAT_MIN_SUBDIR (2) +static const char *dot_name[SDFAT_MIN_SUBDIR] = { DOS_CUR_DIR_NAME, DOS_PAR_DIR_NAME }; + +static s32 __count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type, u32 *dotcnt) +{ + s32 i, count = 0, check_dot = 0; + s32 dentries_per_clu; + u32 entry_type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + else + dentries_per_clu = fsi->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + if (dotcnt) { + *dotcnt = 0; + if (fsi->vol_type != EXFAT) + check_dot = 1; + } + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < dentries_per_clu; i++) { + ep = get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + entry_type = fsi->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_UNUSED) + return count; + if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI)) + continue; + + if ((type != TYPE_ALL) && (type != entry_type)) + continue; + + count++; + if (check_dot && (i < SDFAT_MIN_SUBDIR)) { + BUG_ON(fsi->vol_type == EXFAT); + /* 11 is DOS_NAME_LENGTH */ + if (!strncmp(ep->dummy, dot_name[i], 11)) + (*dotcnt)++; + } + } + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + break; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + + check_dot = 0; + } + + return count; +} + +s32 check_dir_empty(struct super_block *sb, CHAIN_T *p_dir) +{ + s32 i, count = 0; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (IS_CLUS_FREE(p_dir->dir)) /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + else + dentries_per_clu = fsi->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < dentries_per_clu; i++) { + ep = get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + return 0; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + return -ENOTEMPTY; + + if (fsi->vol_type == EXFAT) + return -ENOTEMPTY; + + if ((p_dir->dir == fsi->root_dir) || (++count > 2)) + return -ENOTEMPTY; + } + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + return -ENOTEMPTY; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + } + + return 0; +} + +/* + * Name Conversion Functions + */ +#ifdef CONFIG_SDFAT_ALLOW_LOOKUP_LOSSY_SFN + /* over name length only */ +#define NEED_INVALIDATE_SFN(x) ((x) & NLS_NAME_OVERLEN) +#else + /* all lossy case */ +#define NEED_INVALIDATE_SFN(x) (x) +#endif + +/* NOTE : + * We should keep shortname code compatible with v1.0.15 or lower + * So, we try to check ext-only-name at create-mode only. + * + * i.e. '.mtp' -> + * v1.0.15 : ' MTP' with name_case, 0x10 + * v1.1.0 : 'MT????~?' with name_case, 0x00 and longname. + */ +static inline void preprocess_ext_only_sfn(s32 lookup, u16 first_char, DOS_NAME_T *p_dosname, s32 *lossy) +{ +#ifdef CONFIG_SDFAT_RESTRICT_EXT_ONLY_SFN + int i; + /* check ext-only-name at create-mode */ + if (*lossy || lookup || (first_char != (u16)'.')) + return; + + p_dosname->name_case = 0xFF; + + /* move ext-name to base-name */ + for (i = 0; i < 3; i++) { + p_dosname->name[i] = p_dosname->name[8+i]; + if (p_dosname->name[i] == ' ') + p_dosname->name[i] = '_'; + } + + /* fill remained space with '_' */ + for (i = 3; i < 8; i++) + p_dosname->name[i] = '_'; + + /* eliminate ext-name */ + for (i = 8; i < 11; i++) + p_dosname->name[i] = ' '; + + *lossy = NLS_NAME_LOSSY; +#endif /* CONFIG_SDFAT_CAN_CREATE_EXT_ONLY_SFN */ +} + +/* input : dir, uni_name + * output : num_of_entry, dos_name(format : aaaaaa~1.bbb) + */ +static s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, + UNI_NAME_T *p_uniname, s32 *entries, + DOS_NAME_T *p_dosname, s32 lookup) +{ + s32 ret, num_entries, lossy = NLS_NAME_NO_LOSSY; + s8 **r; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* Init null char. */ + p_dosname->name[0] = '\0'; + + num_entries = fsi->fs_func->calc_num_entries(p_uniname); + if (num_entries == 0) + return -EINVAL; + + if (fsi->vol_type == EXFAT) + goto out; + + nls_uni16s_to_sfn(sb, p_uniname, p_dosname, &lossy); + + preprocess_ext_only_sfn(lookup, p_uniname->name[0], p_dosname, &lossy); + + if (!lossy) { + for (r = reserved_names; *r; r++) { + if (!strncmp((void *) p_dosname->name, *r, 8)) + return -EINVAL; + } + + if (p_dosname->name_case != 0xFF) + num_entries = 1; + } else if (!lookup) { + /* create new dos name */ + ret = fat_generate_dos_name_new(sb, p_dir, p_dosname, + num_entries); + if (ret) + return ret; + + } else if (NEED_INVALIDATE_SFN(lossy)) { + /* FIXME : We should check num_entries */ + p_dosname->name[0] = '\0'; + } + + if (num_entries > 1) + p_dosname->name_case = 0x0; +out: + *entries = num_entries; + return 0; +} /* end of get_num_entries_and_dos_name */ + +void get_uniname_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode) +{ + DOS_NAME_T dos_name; + + if (mode == 0x0) + dos_name.name_case = 0x0; + else + dos_name.name_case = ep->lcase; + + memcpy(dos_name.name, ep->name, DOS_NAME_LENGTH); + nls_sfn_to_uni16s(sb, &dos_name, p_uniname); +} /* end of get_uniname_from_dos_entry */ + +/* returns the length of a struct qstr, ignoring trailing dots */ +static inline unsigned int __striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +/* + * Name Resolution Functions : + * Zero if it was successful; otherwise nonzero. + */ +static s32 __resolve_path(struct inode *inode, const u8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, int lookup) +{ + s32 namelen; + s32 lossy = NLS_NAME_NO_LOSSY; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + + /* DOT and DOTDOT are handled by VFS layer */ + + /* strip all trailing spaces */ + /* DO NOTHING : Is needed? */ + + /* strip all trailing periods */ + namelen = __striptail_len(strlen(path), path); + if (!namelen) + return -ENOENT; + + /* the limitation of linux? */ + if (strlen(path) > (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) + return -ENAMETOOLONG; + + /* + * strip all leading spaces : + * "MS windows 7" supports leading spaces. + * So we should skip this preprocessing for compatibility. + */ + + /* file name conversion : + * If lookup case, we allow bad-name for compatibility. + */ + namelen = nls_vfsname_to_uni16s(sb, path, namelen, p_uniname, &lossy); + if (namelen < 0) + return namelen; /* return error value */ + + if ((lossy && !lookup) || !namelen) + return -EINVAL; + + sdfat_debug_bug_on(fid->size != i_size_read(inode)); +// fid->size = i_size_read(inode); + + p_dir->dir = fid->start_clu; + p_dir->size = (u32)(fid->size >> fsi->cluster_size_bits); + p_dir->flags = fid->flags; + + return 0; +} + +static inline s32 resolve_path(struct inode *inode, const u8 *path, CHAIN_T *dir, UNI_NAME_T *uni) +{ + return __resolve_path(inode, path, dir, uni, 0); +} + +static inline s32 resolve_path_for_lookup(struct inode *inode, const u8 *path, CHAIN_T *dir, UNI_NAME_T *uni) +{ + return __resolve_path(inode, path, dir, uni, 1); +} + +static s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 dentry, num_entries; + u64 ret; + u64 size; + CHAIN_T clu; + DOS_NAME_T dos_name, dot_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name, 0); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return dentry; /* -EIO or -ENOSPC */ + + clu.dir = CLUS_EOF; + clu.size = 0; + clu.flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01; + + /* (0) Check if there are reserved clusters up to max. */ + if ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - CLUS_BASE)) + return -ENOSPC; + + /* (1) allocate a cluster */ + ret = fsi->fs_func->alloc_cluster(sb, 1, &clu, ALLOC_HOT); + if (ret) + return ret; + + ret = __clear_cluster(inode, clu.dir); + if (ret) + return ret; + + size = fsi->cluster_size; + if (fsi->vol_type != EXFAT) { + /* initialize the . and .. entry + * Information for . points to itself + * Information for .. points to parent dir + */ + + dot_name.name_case = 0x0; + memcpy(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH); + + ret = fsi->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0); + if (ret) + return ret; + + ret = fsi->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name); + if (ret) + return ret; + + memcpy(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH); + + if (p_dir->dir == fsi->root_dir) + ret = fsi->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUS_FREE, 0); + else + ret = fsi->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0); + + if (ret) + return ret; + + ret = fsi->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name); + if (ret) + return ret; + } + + /* (2) update the directory entry */ + /* make sub-dir entry in parent directory */ + ret = fsi->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size); + if (ret) + return ret; + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_SUBDIR; + fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = size; + fid->start_clu = clu.dir; + + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + + /* hint_stat will be used if this is directory. */ + fid->version = 0; + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + return 0; +} /* end of create_dir */ + +static s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + DOS_NAME_T dos_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name, 0); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster() */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return dentry; /* -EIO or -ENOSPC */ + + /* (1) update the directory entry */ + /* fill the dos name directory entry information of the created file. + * the first cluster is not determined yet. (0) + */ + ret = fsi->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUS_FREE, 0); + if (ret) + return ret; + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_ARCHIVE | mode; + fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = 0; + fid->start_clu = CLUS_EOF; + + fid->type = TYPE_FILE; + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + + /* hint_stat will be used if this is directory. */ + fid->version = 0; + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + return 0; +} /* end of create_file */ + +static s32 remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry) +{ + s32 num_entries; + u64 sector; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + ep = get_dentry_in_dir(sb, p_dir, entry, §or); + if (!ep) + return -EIO; + + dcache_lock(sb, sector); + + /* dcache_lock() before call count_ext_entries() */ + num_entries = fsi->fs_func->count_ext_entries(sb, p_dir, entry, ep); + if (num_entries < 0) { + dcache_unlock(sb, sector); + return -EIO; + } + num_entries++; + + dcache_unlock(sb, sector); + + /* (1) update the directory entry */ + return fsi->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); +} /* end of remove_file */ + +static s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry = -1, num_old_entries, num_new_entries; + u64 sector_old, sector_new; + DOS_NAME_T dos_name; + DENTRY_T *epold, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + epold = get_dentry_in_dir(sb, p_dir, oldentry, §or_old); + if (!epold) + return -EIO; + + dcache_lock(sb, sector_old); + + /* dcache_lock() before call count_ext_entries() */ + num_old_entries = fsi->fs_func->count_ext_entries(sb, p_dir, oldentry, epold); + if (num_old_entries < 0) { + dcache_unlock(sb, sector_old); + return -EIO; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name, 0); + if (ret) { + dcache_unlock(sb, sector_old); + return ret; + } + + if (num_old_entries < num_new_entries) { + newentry = find_empty_entry(inode, p_dir, num_new_entries); + if (newentry < 0) { + dcache_unlock(sb, sector_old); + return newentry; /* -EIO or -ENOSPC */ + } + + epnew = get_dentry_in_dir(sb, p_dir, newentry, §or_new); + if (!epnew) { + dcache_unlock(sb, sector_old); + return -EIO; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + if (fsi->fs_func->get_entry_type(epnew) == TYPE_FILE) { + fsi->fs_func->set_entry_attr(epnew, fsi->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + dcache_modify(sb, sector_new); + dcache_unlock(sb, sector_old); + + if (fsi->vol_type == EXFAT) { + epold = get_dentry_in_dir(sb, p_dir, oldentry+1, §or_old); + dcache_lock(sb, sector_old); + epnew = get_dentry_in_dir(sb, p_dir, newentry+1, §or_new); + + if (!epold || !epnew) { + dcache_unlock(sb, sector_old); + return -EIO; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + dcache_modify(sb, sector_new); + dcache_unlock(sb, sector_old); + } + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fsi->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); + fid->entry = newentry; + } else { + if (fsi->fs_func->get_entry_type(epold) == TYPE_FILE) { + fsi->fs_func->set_entry_attr(epold, fsi->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + dcache_modify(sb, sector_old); + dcache_unlock(sb, sector_old); + + ret = fsi->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fsi->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); + } + + return 0; +} /* end of rename_file */ + +static s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, + CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry, num_new_entries, num_old_entries; + u64 sector_mov, sector_new; + CHAIN_T clu; + DOS_NAME_T dos_name; + DENTRY_T *epmov, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + epmov = get_dentry_in_dir(sb, p_olddir, oldentry, §or_mov); + if (!epmov) + return -EIO; + + /* check if the source and target directory is the same */ + if (fsi->fs_func->get_entry_type(epmov) == TYPE_DIR && + fsi->fs_func->get_entry_clu0(epmov) == p_newdir->dir) + return -EINVAL; + + dcache_lock(sb, sector_mov); + + /* dcache_lock() before call count_ext_entries() */ + num_old_entries = fsi->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov); + if (num_old_entries < 0) { + dcache_unlock(sb, sector_mov); + return -EIO; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name, 0); + if (ret) { + dcache_unlock(sb, sector_mov); + return ret; + } + + newentry = find_empty_entry(inode, p_newdir, num_new_entries); + if (newentry < 0) { + dcache_unlock(sb, sector_mov); + return newentry; /* -EIO or -ENOSPC */ + } + + epnew = get_dentry_in_dir(sb, p_newdir, newentry, §or_new); + if (!epnew) { + dcache_unlock(sb, sector_mov); + return -EIO; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + if (fsi->fs_func->get_entry_type(epnew) == TYPE_FILE) { + fsi->fs_func->set_entry_attr(epnew, fsi->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + dcache_modify(sb, sector_new); + dcache_unlock(sb, sector_mov); + + if (fsi->vol_type == EXFAT) { + epmov = get_dentry_in_dir(sb, p_olddir, oldentry+1, §or_mov); + dcache_lock(sb, sector_mov); + epnew = get_dentry_in_dir(sb, p_newdir, newentry+1, §or_new); + if (!epmov || !epnew) { + dcache_unlock(sb, sector_mov); + return -EIO; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + dcache_modify(sb, sector_new); + dcache_unlock(sb, sector_mov); + } else if (fsi->fs_func->get_entry_type(epnew) == TYPE_DIR) { + /* change ".." pointer to new parent dir */ + clu.dir = fsi->fs_func->get_entry_clu0(epnew); + clu.flags = 0x01; + + epnew = get_dentry_in_dir(sb, &clu, 1, §or_new); + if (!epnew) + return -EIO; + + if (p_newdir->dir == fsi->root_dir) + fsi->fs_func->set_entry_clu0(epnew, CLUS_FREE); + else + fsi->fs_func->set_entry_clu0(epnew, p_newdir->dir); + dcache_modify(sb, sector_new); + } + + ret = fsi->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret) + return ret; + + fsi->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries); + + fid->dir.dir = p_newdir->dir; + fid->dir.size = p_newdir->size; + fid->dir.flags = p_newdir->flags; + + fid->entry = newentry; + + return 0; +} /* end of move_file */ + + +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ +/* roll back to the initial state of the file system */ +s32 fscore_init(void) +{ + s32 ret; + + ret = check_type_size(); + if (ret) + return ret; + + return extent_cache_init(); +} + +/* make free all memory-alloced global buffers */ +s32 fscore_shutdown(void) +{ + extent_cache_shutdown(); + return 0; +} + +static bool is_exfat(pbr_t *pbr) +{ + int i = 53; + + do { + if (pbr->bpb.f64.res_zero[i-1]) + break; + } while (--i); + return i ? false : true; +} + +static bool is_fat32(pbr_t *pbr) +{ + if (le16_to_cpu(pbr->bpb.f16.num_fat_sectors)) + return false; + return true; +} + +inline pbr_t *read_pbr_with_logical_sector(struct super_block *sb, struct buffer_head **prev_bh) +{ + pbr_t *p_pbr = (pbr_t *) (*prev_bh)->b_data; + u16 logical_sect = 0; + + if (is_exfat(p_pbr)) + logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits; + else + logical_sect = get_unaligned_le16(&p_pbr->bpb.f16.sect_size); + + /* is x a power of 2? + * (x) != 0 && (((x) & ((x) - 1)) == 0) + */ + if (!is_power_of_2(logical_sect) + || (logical_sect < 512) + || (logical_sect > 4096)) { + sdfat_log_msg(sb, KERN_ERR, "bogus logical sector size %u", + logical_sect); + return NULL; + } + + if (logical_sect < sb->s_blocksize) { + sdfat_log_msg(sb, KERN_ERR, + "logical sector size too small for device" + " (logical sector size = %u)", logical_sect); + return NULL; + } + + if (logical_sect > sb->s_blocksize) { + struct buffer_head *bh = NULL; + + __brelse(*prev_bh); + *prev_bh = NULL; + + if (!sb_set_blocksize(sb, logical_sect)) { + sdfat_log_msg(sb, KERN_ERR, + "unable to set blocksize %u", logical_sect); + return NULL; + } + bh = sb_bread(sb, 0); + if (!bh) { + sdfat_log_msg(sb, KERN_ERR, + "unable to read boot sector " + "(logical sector size = %lu)", sb->s_blocksize); + return NULL; + } + + *prev_bh = bh; + p_pbr = (pbr_t *) bh->b_data; + } + + sdfat_log_msg(sb, KERN_INFO, + "set logical sector size : %lu", sb->s_blocksize); + + return p_pbr; +} + +/* mount the file system volume */ +s32 fscore_mount(struct super_block *sb) +{ + s32 ret; + pbr_t *p_pbr; + struct buffer_head *tmp_bh = NULL; + struct gendisk *disk = sb->s_bdev->bd_disk; + struct sdfat_mount_options *opts = &(SDFAT_SB(sb)->options); + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* initialize previous I/O error */ + fsi->prev_eio = 0; + + /* open the block device */ + if (bdev_open_dev(sb)) + return -EIO; + + /* set block size to read super block */ + sb_min_blocksize(sb, 512); + + /* read boot sector */ + ret = read_sect(sb, 0, &tmp_bh, 1); + if (ret) { + sdfat_log_msg(sb, KERN_ERR, "unable to read boot sector"); + ret = -EIO; + goto bd_close; + } + + /* PRB is read */ + p_pbr = (pbr_t *) tmp_bh->b_data; + + /* check the validity of PBR */ + if (le16_to_cpu((p_pbr->signature)) != PBR_SIGNATURE) { + sdfat_log_msg(sb, KERN_ERR, "invalid boot record signature"); + brelse(tmp_bh); + ret = -EINVAL; + goto bd_close; + } + + /* check logical sector size */ + p_pbr = read_pbr_with_logical_sector(sb, &tmp_bh); + if (!p_pbr) { + brelse(tmp_bh); + ret = -EIO; + goto bd_close; + } + + /* fill fs_struct */ + if (is_exfat(p_pbr)) { + if (opts->fs_type && opts->fs_type != FS_TYPE_EXFAT) { + sdfat_log_msg(sb, KERN_ERR, + "not specified filesystem type " + "(media:exfat, opts:%s)", + FS_TYPE_STR[opts->fs_type]); + ret = -EINVAL; + goto free_bh; + } + /* set maximum file size for exFAT */ + sb->s_maxbytes = 0x7fffffffffffffffLL; + opts->improved_allocation = 0; + opts->defrag = 0; + ret = mount_exfat(sb, p_pbr); + } else if (is_fat32(p_pbr)) { + if (opts->fs_type && opts->fs_type != FS_TYPE_VFAT) { + sdfat_log_msg(sb, KERN_ERR, + "not specified filesystem type " + "(media:vfat, opts:%s)", + FS_TYPE_STR[opts->fs_type]); + ret = -EINVAL; + goto free_bh; + } + /* set maximum file size for FAT */ + sb->s_maxbytes = 0xffffffff; + ret = mount_fat32(sb, p_pbr); + } else { + if (opts->fs_type && opts->fs_type != FS_TYPE_VFAT) { + sdfat_log_msg(sb, KERN_ERR, + "not specified filesystem type " + "(media:vfat, opts:%s)", + FS_TYPE_STR[opts->fs_type]); + ret = -EINVAL; + goto free_bh; + } + /* set maximum file size for FAT */ + sb->s_maxbytes = 0xffffffff; + opts->improved_allocation = 0; + opts->defrag = 0; + ret = mount_fat16(sb, p_pbr); + } +free_bh: + brelse(tmp_bh); + if (ret) { + sdfat_log_msg(sb, KERN_ERR, "failed to mount fs-core"); + goto bd_close; + } + + /* warn misaligned data data start sector must be a multiple of clu_size */ + sdfat_log_msg(sb, KERN_INFO, + "detected volume info : %s " + "(bps : %lu, spc : %u, data start : %llu, %s)", + sdfat_get_vol_type_str(fsi->vol_type), + sb->s_blocksize, fsi->sect_per_clus, fsi->data_start_sector, + (fsi->data_start_sector & (fsi->sect_per_clus - 1)) ? + "misaligned" : "aligned"); + + sdfat_log_msg(sb, KERN_INFO, + "detected volume size : %llu MB (disk_size : %llu MB)", + fsi->num_sectors >> 11, + disk ? (u64)((disk->part0.nr_sects) >> 11) : 0); + + ret = load_upcase_table(sb); + if (ret) { + sdfat_log_msg(sb, KERN_ERR, "failed to load upcase table"); + goto bd_close; + } + + if (fsi->vol_type != EXFAT) + goto update_used_clus; + + /* allocate-bitmap is only for exFAT */ + ret = load_alloc_bmp(sb); + if (ret) { + sdfat_log_msg(sb, KERN_ERR, "failed to load alloc-bitmap"); + goto free_upcase; + } + +update_used_clus: + if (fsi->used_clusters == (u32) ~0) { + ret = fsi->fs_func->count_used_clusters(sb, &fsi->used_clusters); + if (ret) { + sdfat_log_msg(sb, KERN_ERR, "failed to scan clusters"); + goto free_alloc_bmp; + } + } + + return 0; +free_alloc_bmp: + if (fsi->vol_type == EXFAT) + free_alloc_bmp(sb); +free_upcase: + free_upcase_table(sb); +bd_close: + bdev_close_dev(sb); + return ret; +} /* end of fscore_mount */ + +/* umount the file system volume */ +s32 fscore_umount(struct super_block *sb) +{ + s32 ret = 0; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (fs_sync(sb, 0)) + ret = -EIO; + + if (fs_set_vol_flags(sb, VOL_CLEAN)) + ret = -EIO; + + free_upcase_table(sb); + + if (fsi->vol_type == EXFAT) + free_alloc_bmp(sb); + + if (fcache_release_all(sb)) + ret = -EIO; + + if (dcache_release_all(sb)) + ret = -EIO; + + amap_destroy(sb); + + if (fsi->prev_eio) + ret = -EIO; + /* close the block device */ + bdev_close_dev(sb); + return ret; +} + +/* get the information of a file system volume */ +s32 fscore_statfs(struct super_block *sb, VOL_INFO_T *info) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (fsi->used_clusters == (u32) ~0) { + if (fsi->fs_func->count_used_clusters(sb, &fsi->used_clusters)) + return -EIO; + } + + info->FatType = fsi->vol_type; + info->ClusterSize = fsi->cluster_size; + info->NumClusters = fsi->num_clusters - 2; /* clu 0 & 1 */ + info->UsedClusters = fsi->used_clusters + fsi->reserved_clusters; + info->FreeClusters = info->NumClusters - info->UsedClusters; + + return 0; +} + +/* synchronize all file system volumes */ +s32 fscore_sync_fs(struct super_block *sb, s32 do_sync) +{ + /* synchronize the file system */ + if (fs_sync(sb, do_sync)) + return -EIO; + + if (fs_set_vol_flags(sb, VOL_CLEAN)) + return -EIO; + + return 0; +} + +/* stat allocation unit of a file system volume */ +u32 fscore_get_au_stat(struct super_block *sb, s32 mode) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (fsi->fs_func->get_au_stat) + return fsi->fs_func->get_au_stat(sb, mode); + + /* No error, just returns 0 */ + return 0; +} + + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ +/* lookup a file */ +s32 fscore_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + CHAIN_T dir; + UNI_NAME_T uni_name; + DOS_NAME_T dos_name; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *dir_fid = &(SDFAT_I(inode)->fid); + + TMSG("%s entered\n", __func__); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path_for_lookup(inode, path, &dir, &uni_name); + if (ret) + return ret; + + ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name, 1); + if (ret) + return ret; + + /* check the validation of hint_stat and initialize it if required */ + if (dir_fid->version != (u32)(inode->i_version & 0xffffffff)) { + dir_fid->hint_stat.clu = dir.dir; + dir_fid->hint_stat.eidx = 0; + dir_fid->version = (u32)(inode->i_version & 0xffffffff); + dir_fid->hint_femp.eidx = -1; + } + + /* search the file name for directories */ + dentry = fsi->fs_func->find_dir_entry(sb, dir_fid, &dir, &uni_name, + num_entries, &dos_name, TYPE_ALL); + + if ((dentry < 0) && (dentry != -EEXIST)) + return dentry; /* -error value */ + + fid->dir.dir = dir.dir; + fid->dir.size = dir.size; + fid->dir.flags = dir.flags; + fid->entry = dentry; + + /* root directory itself */ + if (unlikely(dentry == -EEXIST)) { + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + + fid->attr = ATTR_SUBDIR; + fid->flags = 0x01; + fid->size = 0; + fid->start_clu = fsi->root_dir; + } else { + if (fsi->vol_type == EXFAT) { + es = get_dentry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + } else { + ep = get_dentry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return -EIO; + ep2 = ep; + } + + fid->type = fsi->fs_func->get_entry_type(ep); + fid->rwoffset = 0; + fid->hint_bmap.off = CLUS_EOF; + fid->attr = fsi->fs_func->get_entry_attr(ep); + + fid->size = fsi->fs_func->get_entry_size(ep2); + if ((fid->type == TYPE_FILE) && (fid->size == 0)) { + fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUS_EOF; + } else { + fid->flags = fsi->fs_func->get_entry_flag(ep2); + fid->start_clu = fsi->fs_func->get_entry_clu0(ep2); + } + + if ((fid->type == TYPE_DIR) && (fsi->vol_type != EXFAT)) { + u32 num_clu = 0; + CHAIN_T tmp_dir; + + tmp_dir.dir = fid->start_clu; + tmp_dir.flags = fid->flags; + tmp_dir.size = 0; /* UNUSED */ + + if (__count_num_clusters(sb, &tmp_dir, &num_clu)) + return -EIO; + fid->size = (u64)num_clu << fsi->cluster_size_bits; + } + + /* FOR GRACEFUL ERROR HANDLING */ + if (IS_CLUS_FREE(fid->start_clu)) { + sdfat_fs_error(sb, + "non-zero size file starts with zero cluster " + "(size : %llu, p_dir : %u, entry : 0x%08x)", + fid->size, fid->dir.dir, fid->entry); + sdfat_debug_bug_on(1); + return -EIO; + } + + if (fsi->vol_type == EXFAT) + release_dentry_set(es); + } + + /* hint_stat will be used if this is directory. */ + fid->version = 0; + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + TMSG("%s exited successfully\n", __func__); + return 0; +} /* end of fscore_lookup */ + +/* create a file */ +s32 fscore_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* create a new file */ + ret = create_file(inode, &dir, &uni_name, mode, fid); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} + +/* read data from a opened file */ +s32 fscore_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + s32 ret = 0; + s32 offset, sec_offset; + u32 clu_offset; + u32 clu; + u64 logsector, oneblkread, read_bytes; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return -EPERM; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count > (fid->size - fid->rwoffset)) + count = fid->size - fid->rwoffset; + + if (count == 0) { + if (rcount) + *rcount = 0; + return 0; + } + + read_bytes = 0; + + while (count > 0) { + clu_offset = fid->rwoffset >> fsi->cluster_size_bits; + clu = fid->start_clu; + + if (fid->flags == 0x03) { + clu += clu_offset; + } else { + /* hint information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + clu = fid->hint_bmap.clu; + } + + while (clu_offset > 0) { + ret = get_next_clus_safe(sb, &clu); + if (ret) + goto err_out; + + clu_offset--; + } + } + + /* hint information */ + fid->hint_bmap.off = fid->rwoffset >> fsi->cluster_size_bits; + fid->hint_bmap.clu = clu; + + offset = (s32)(fid->rwoffset & (fsi->cluster_size - 1)); /* byte offset in cluster */ + sec_offset = offset >> sb->s_blocksize_bits; /* sector offset in cluster */ + offset &= (sb->s_blocksize - 1); /* byte offset in sector */ + + logsector = CLUS_TO_SECT(fsi, clu) + sec_offset; + + oneblkread = (u64)(sb->s_blocksize - offset); + if (oneblkread > count) + oneblkread = count; + + if ((offset == 0) && (oneblkread == sb->s_blocksize)) { + ret = read_sect(sb, logsector, &tmp_bh, 1); + if (ret) + goto err_out; + memcpy(((s8 *) buffer)+read_bytes, ((s8 *) tmp_bh->b_data), (s32) oneblkread); + } else { + ret = read_sect(sb, logsector, &tmp_bh, 1); + if (ret) + goto err_out; + memcpy(((s8 *) buffer)+read_bytes, ((s8 *) tmp_bh->b_data)+offset, (s32) oneblkread); + } + count -= oneblkread; + read_bytes += oneblkread; + fid->rwoffset += oneblkread; + } + +err_out: + brelse(tmp_bh); + + /* set the size of read bytes */ + if (rcount != NULL) + *rcount = read_bytes; + + return ret; +} /* end of fscore_read_link */ + +/* write data into a opened file */ +s32 fscore_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + s32 ret = 0; + s32 modified = false, offset, sec_offset; + u32 clu_offset, num_clusters, num_alloc; + u32 clu, last_clu; + u64 logsector, sector, oneblkwrite, write_bytes; + CHAIN_T new_clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + u32 blksize = (u32)sb->s_blocksize; + u32 blksize_mask = (u32)(sb->s_blocksize-1); + u8 blksize_bits = sb->s_blocksize_bits; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return -EPERM; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count == 0) { + if (wcount) + *wcount = 0; + return 0; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (fid->size == 0) + num_clusters = 0; + else + num_clusters = ((fid->size-1) >> fsi->cluster_size_bits) + 1; + + write_bytes = 0; + + while (count > 0) { + clu_offset = (fid->rwoffset >> fsi->cluster_size_bits); + clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (!IS_CLUS_EOF(clu))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + clu = CLUS_EOF; + else + clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + clu = fid->hint_bmap.clu; + } + + while ((clu_offset > 0) && (!IS_CLUS_EOF(clu))) { + last_clu = clu; + ret = get_next_clus_safe(sb, &clu); + if (ret) + goto err_out; + + clu_offset--; + } + } + + if (IS_CLUS_EOF(clu)) { + num_alloc = ((count-1) >> fsi->cluster_size_bits) + 1; + new_clu.dir = IS_CLUS_EOF(last_clu) ? CLUS_EOF : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a chain of clusters */ + ret = fsi->fs_func->alloc_cluster(sb, num_alloc, &new_clu, ALLOC_COLD); + if (ret) + goto err_out; + + /* (2) append to the FAT chain */ + if (IS_CLUS_EOF(last_clu)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = true; + } else { + if (new_clu.flags != fid->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with + * alloc-bmp + */ + chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = true; + } + if (new_clu.flags == 0x01) { + ret = fat_ent_set(sb, last_clu, new_clu.dir); + if (ret) + goto err_out; + } + } + + num_clusters += num_alloc; + clu = new_clu.dir; + } + + /* hint information */ + fid->hint_bmap.off = fid->rwoffset >> fsi->cluster_size_bits; + fid->hint_bmap.clu = clu; + + /* byte offset in cluster */ + offset = (s32)(fid->rwoffset & (fsi->cluster_size-1)); + /* sector offset in cluster */ + sec_offset = offset >> blksize_bits; + /* byte offset in sector */ + offset &= blksize_mask; + logsector = CLUS_TO_SECT(fsi, clu) + sec_offset; + + oneblkwrite = (u64)(blksize - offset); + if (oneblkwrite > count) + oneblkwrite = count; + + if ((offset == 0) && (oneblkwrite == blksize)) { + ret = read_sect(sb, logsector, &tmp_bh, 0); + if (ret) + goto err_out; + + memcpy(((s8 *)tmp_bh->b_data), + ((s8 *)buffer)+write_bytes, + (s32)oneblkwrite); + + ret = write_sect(sb, logsector, tmp_bh, 0); + if (ret) { + brelse(tmp_bh); + goto err_out; + } + } else { + if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { + ret = read_sect(sb, logsector, &tmp_bh, 1); + if (ret) + goto err_out; + } else { + ret = read_sect(sb, logsector, &tmp_bh, 0); + if (ret) + goto err_out; + } + + memcpy(((s8 *) tmp_bh->b_data)+offset, ((s8 *) buffer)+write_bytes, (s32) oneblkwrite); + ret = write_sect(sb, logsector, tmp_bh, 0); + if (ret) { + brelse(tmp_bh); + goto err_out; + } + } + + count -= oneblkwrite; + write_bytes += oneblkwrite; + fid->rwoffset += oneblkwrite; + + fid->attr |= ATTR_ARCHIVE; + + if (fid->size < fid->rwoffset) { + fid->size = fid->rwoffset; + modified = true; + } + } + + brelse(tmp_bh); + + /* (3) update the direcoty entry */ + /* get_entry_(set_)in_dir shoulb be check DIR_DELETED flag. */ + if (fsi->vol_type == EXFAT) { + es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) { + ret = -EIO; + goto err_out; + } + ep2 = ep+1; + } else { + ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) { + ret = -EIO; + goto err_out; + } + ep2 = ep; + } + + fsi->fs_func->set_entry_time(ep, tm_now(SDFAT_SB(sb), &tm), TM_MODIFY); + fsi->fs_func->set_entry_attr(ep, fid->attr); + + if (modified) { + if (fsi->fs_func->get_entry_flag(ep2) != fid->flags) + fsi->fs_func->set_entry_flag(ep2, fid->flags); + + if (fsi->fs_func->get_entry_size(ep2) != fid->size) + fsi->fs_func->set_entry_size(ep2, fid->size); + + if (fsi->fs_func->get_entry_clu0(ep2) != fid->start_clu) + fsi->fs_func->set_entry_clu0(ep2, fid->start_clu); + } + + if (fsi->vol_type == EXFAT) { + if (update_dir_chksum_with_entry_set(sb, es)) { + ret = -EIO; + goto err_out; + } + release_dentry_set(es); + } else { + if (dcache_modify(sb, sector)) { + ret = -EIO; + goto err_out; + } + } + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + +err_out: + /* set the size of written bytes */ + if (wcount) + *wcount = write_bytes; + + return ret; +} /* end of fscore_write_link */ + +/* resize the file length */ +s32 fscore_truncate(struct inode *inode, u64 old_size, u64 new_size) +{ + u32 num_clusters_new, num_clusters_da, num_clusters_phys; + u32 last_clu = CLUS_FREE; + u64 sector; + CHAIN_T clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + s32 evict = (fid->dir.dir == DIR_DELETED) ? 1 : 0; + + /* check if the given file ID is opened */ + if ((fid->type != TYPE_FILE) && (fid->type != TYPE_DIR)) + return -EPERM; + + /* TO CHECK inode type and size */ + MMSG("%s: inode(%p) type(%s) size:%lld->%lld\n", __func__, inode, + (fid->type == TYPE_FILE) ? "file" : "dir", old_size, new_size); + + /* XXX : This is for debugging. */ + + /* It can be when write failed */ +#if 0 + if (fid->size != old_size) { + DMSG("%s: inode(%p) size-mismatch(old:%lld != fid:%lld)\n", + __func__, inode, old_size, fid->size); + WARN_ON(1); + } +#endif + /* + * There is no lock to protect fid->size. + * So, we should get old_size and use it. + */ + if (old_size <= new_size) + return 0; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* Reserved count update */ + #define num_clusters(v) ((v) ? (u32)(((v) - 1) >> fsi->cluster_size_bits) + 1 : 0) + num_clusters_da = num_clusters(SDFAT_I(inode)->i_size_aligned); + num_clusters_new = num_clusters(i_size_read(inode)); + num_clusters_phys = num_clusters(SDFAT_I(inode)->i_size_ondisk); + + /* num_clusters(i_size_old) should be equal to num_clusters_da */ + BUG_ON((num_clusters(old_size)) != (num_clusters(SDFAT_I(inode)->i_size_aligned))); + + /* for debugging (FIXME: is okay on no-da case?) */ + BUG_ON(num_clusters_da < num_clusters_phys); + + if ((num_clusters_da != num_clusters_phys) && + (num_clusters_new < num_clusters_da)) { + /* Decrement reserved clusters + * n_reserved = num_clusters_da - max(new,phys) + */ + int n_reserved = (num_clusters_new > num_clusters_phys) ? + (num_clusters_da - num_clusters_new) : + (num_clusters_da - num_clusters_phys); + + fsi->reserved_clusters -= n_reserved; + BUG_ON(fsi->reserved_clusters < 0); + } + + clu.dir = fid->start_clu; + /* In no-da case, num_clusters_phys is equal to below value + * clu.size = (u32)((old_size-1) >> fsi->cluster_size_bits) + 1; + */ + clu.size = num_clusters_phys; + clu.flags = fid->flags; + + /* For bigdata */ + sdfat_statistics_set_trunc(clu.flags, &clu); + + if (new_size > 0) { + /* Truncate FAT chain num_clusters after the first cluster + * num_clusters = min(new, phys); + */ + u32 num_clusters = (num_clusters_new < num_clusters_phys) ? + num_clusters_new : num_clusters_phys; + + /* Follow FAT chain + * (defensive coding - works fine even with corrupted FAT table + */ + if (clu.flags == 0x03) { + clu.dir += num_clusters; + clu.size -= num_clusters; +#if 0 + /* extent_get_clus can`t know last_cluster + * when find target cluster in cache. + */ + } else if (fid->type == TYPE_FILE) { + u32 fclus = 0; + s32 err = extent_get_clus(inode, num_clusters, + &fclus, &(clu.dir), &last_clu, 0); + if (err) + return -EIO; + ASSERT(fclus == num_clusters); + + if ((num_clusters > 1) && (last_clu == fid->start_clu)) { + u32 fclus_tmp = 0; + u32 temp = 0; + + err = extent_get_clus(inode, num_clusters - 1, + &fclus_tmp, &last_clu, &temp, 0); + if (err) + return -EIO; + ASSERT(fclus_tmp == (num_clusters - 1)); + } + + num_clusters -= fclus; + clu.size -= fclus; +#endif + } else { + while (num_clusters > 0) { + last_clu = clu.dir; + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + + num_clusters--; + clu.size--; + } + } + + /* Optimization avialable: */ +#if 0 + if (num_clusters_new < num_clusters) { + < loop > + } else { + // num_clusters_new >= num_clusters_phys + // FAT truncation is not necessary + + clu.dir = CLUS_EOF; + clu.size = 0; + } +#endif + } else if (new_size == 0) { + fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUS_EOF; + } + fid->size = new_size; + + if (fid->type == TYPE_FILE) + fid->attr |= ATTR_ARCHIVE; + + /* + * clu.dir: free from + * clu.size: # of clusters to free (exFAT, 0x03 only), no fat_free if 0 + * clu.flags: fid->flags (exFAT only) + */ + + /* (1) update the directory entry */ + if (!evict) { + + if (fsi->vol_type == EXFAT) { + es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + } else { + ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return -EIO; + ep2 = ep; + } + + fsi->fs_func->set_entry_time(ep, tm_now(SDFAT_SB(sb), &tm), TM_MODIFY); + fsi->fs_func->set_entry_attr(ep, fid->attr); + + /* + * if (fsi->vol_type != EXFAT) + * dcache_modify(sb, sector); + */ + + /* File size should be zero if there is no cluster allocated */ + if (IS_CLUS_EOF(fid->start_clu)) + fsi->fs_func->set_entry_size(ep2, 0); + else + fsi->fs_func->set_entry_size(ep2, new_size); + + if (new_size == 0) { + /* Any directory can not be truncated to zero */ + BUG_ON(fid->type != TYPE_FILE); + + fsi->fs_func->set_entry_flag(ep2, 0x01); + fsi->fs_func->set_entry_clu0(ep2, CLUS_FREE); + } + + if (fsi->vol_type == EXFAT) { + if (update_dir_chksum_with_entry_set(sb, es)) + return -EIO; + release_dentry_set(es); + } else { + if (dcache_modify(sb, sector)) + return -EIO; + } + + } /* end of if(fid->dir.dir != DIR_DELETED) */ + + /* (2) cut off from the FAT chain */ + if ((fid->flags == 0x01) && + (!IS_CLUS_FREE(last_clu)) && (!IS_CLUS_EOF(last_clu))) { + if (fat_ent_set(sb, last_clu, CLUS_EOF)) + return -EIO; + } + + /* (3) invalidate cache and free the clusters */ + /* clear extent cache */ + extent_cache_inval_inode(inode); + + /* hint information */ + fid->hint_bmap.off = CLUS_EOF; + fid->hint_bmap.clu = CLUS_EOF; + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + /* hint_stat will be used if this is directory. */ + fid->hint_stat.eidx = 0; + fid->hint_stat.clu = fid->start_clu; + fid->hint_femp.eidx = -1; + + /* free the clusters */ + if (fsi->fs_func->free_cluster(sb, &clu, evict)) + return -EIO; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return 0; +} /* end of fscore_truncate */ + +static void update_parent_info(FILE_ID_T *fid, struct inode *parent_inode) +{ + FS_INFO_T *fsi = &(SDFAT_SB(parent_inode->i_sb)->fsi); + FILE_ID_T *parent_fid = &(SDFAT_I(parent_inode)->fid); + + /* + * the problem that FILE_ID_T caches wrong parent info. + * + * because of flag-mismatch of fid->dir, + * there is abnormal traversing cluster chain. + */ + if (unlikely((parent_fid->flags != fid->dir.flags) + || (parent_fid->size != (fid->dir.size<cluster_size_bits)) + || (parent_fid->start_clu != fid->dir.dir))) { + + fid->dir.dir = parent_fid->start_clu; + fid->dir.flags = parent_fid->flags; + fid->dir.size = ((parent_fid->size + (fsi->cluster_size-1)) + >> fsi->cluster_size_bits); + } +} + +/* rename or move a old file into a new file */ +s32 fscore_rename(struct inode *old_parent_inode, FILE_ID_T *fid, + struct inode *new_parent_inode, struct dentry *new_dentry) +{ + s32 ret; + s32 dentry; + CHAIN_T olddir, newdir; + CHAIN_T *p_dir = NULL; + UNI_NAME_T uni_name; + DENTRY_T *ep; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u8 *new_path = (u8 *) new_dentry->d_name.name; + struct inode *new_inode = new_dentry->d_inode; + int num_entries; + FILE_ID_T *new_fid = NULL; + u32 new_entry_type = TYPE_UNUSED; + s32 new_entry = 0; + + /* check the validity of pointer parameters */ + if ((new_path == NULL) || (strlen(new_path) == 0)) + return -EINVAL; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted source dentry\n", __func__); + return -ENOENT; + } + + /* patch 1.2.4 : the problem that FILE_ID_T caches wrong parent info. */ + update_parent_info(fid, old_parent_inode); + + olddir.dir = fid->dir.dir; + olddir.size = fid->dir.size; + olddir.flags = fid->dir.flags; + + dentry = fid->entry; + + /* check if the old file is "." or ".." */ + if (fsi->vol_type != EXFAT) { + if ((olddir.dir != fsi->root_dir) && (dentry < 2)) + return -EPERM; + } + + ep = get_dentry_in_dir(sb, &olddir, dentry, NULL); + if (!ep) + return -EIO; + +#ifdef CONFIG_SDFAT_CHECK_RO_ATTR + if (fsi->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; +#endif + + /* check whether new dir is existing directory and empty */ + if (new_inode) { + ret = -EIO; + new_fid = &SDFAT_I(new_inode)->fid; + + if (new_fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted target dentry\n", __func__); + goto out; + } + + /* patch 1.2.4 : + * the problem that FILE_ID_T caches wrong parent info. + * + * FIXME : is needed? + */ + update_parent_info(new_fid, new_parent_inode); + + p_dir = &(new_fid->dir); + new_entry = new_fid->entry; + ep = get_dentry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + new_entry_type = fsi->fs_func->get_entry_type(ep); + + /* if new_inode exists, update fid */ + new_fid->size = i_size_read(new_inode); + + if (new_entry_type == TYPE_DIR) { + CHAIN_T new_clu; + + new_clu.dir = new_fid->start_clu; + new_clu.size = ((new_fid->size-1) >> fsi->cluster_size_bits) + 1; + new_clu.flags = new_fid->flags; + + ret = check_dir_empty(sb, &new_clu); + if (ret) + return ret; + } + } + + /* check the validity of directory name in the given new pathname */ + ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (olddir.dir == newdir.dir) + ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid); + else + ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); + + if ((!ret) && new_inode) { + /* delete entries of new_dir */ + ep = get_dentry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) { + ret = -EIO; + goto del_out; + } + + num_entries = fsi->fs_func->count_ext_entries(sb, p_dir, new_entry, ep); + if (num_entries < 0) { + ret = -EIO; + goto del_out; + } + + + if (fsi->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1)) { + ret = -EIO; + goto del_out; + } + + /* Free the clusters if new_inode is a dir(as if fscore_rmdir) */ + if (new_entry_type == TYPE_DIR) { + /* new_fid, new_clu_to_free */ + CHAIN_T new_clu_to_free; + + new_clu_to_free.dir = new_fid->start_clu; + new_clu_to_free.size = ((new_fid->size-1) >> fsi->cluster_size_bits) + 1; + new_clu_to_free.flags = new_fid->flags; + + if (fsi->fs_func->free_cluster(sb, &new_clu_to_free, 1)) { + /* just set I/O error only */ + ret = -EIO; + } + + new_fid->size = 0; + new_fid->start_clu = CLUS_EOF; + new_fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01; + } +del_out: + /* Update new_inode fid + * Prevent syncing removed new_inode + * (new_fid is already initialized above code ("if (new_inode)") + */ + new_fid->dir.dir = DIR_DELETED; + } +out: + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} /* end of fscore_rename */ + +/* remove a file */ +s32 fscore_remove(struct inode *inode, FILE_ID_T *fid) +{ + s32 ret; + s32 dentry; + CHAIN_T dir, clu_to_free; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + return -ENOENT; + } + + ep = get_dentry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return -EIO; + + +#ifdef CONFIG_SDFAT_CHECK_RO_ATTR + if (fsi->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; +#endif + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + ret = remove_file(inode, &dir, dentry); + if (ret) + goto out; + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = ((fid->size-1) >> fsi->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + /* (2) invalidate extent cache and free the clusters + */ + /* clear extent cache */ + extent_cache_inval_inode(inode); + ret = fsi->fs_func->free_cluster(sb, &clu_to_free, 0); + /* WARN : DO NOT RETURN ERROR IN HERE */ + + /* (3) update FILE_ID_T */ + fid->size = 0; + fid->start_clu = CLUS_EOF; + fid->flags = (fsi->vol_type == EXFAT) ? 0x03 : 0x01; + fid->dir.dir = DIR_DELETED; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +out: + return ret; +} /* end of fscore_remove */ + + +/* + * Get the information of a given file + * REMARK : This function does not need any file name on linux + * + * info.Size means the value saved on disk. + * But root directory doesn`t have real dentry, + * so the size of root directory returns calculated one exceptively. + */ +s32 fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info) +{ + u64 sector; + s32 count; + CHAIN_T dir; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + TMSG("%s entered\n", __func__); + + extent_cache_init_inode(inode); + + /* if root directory */ + if (is_dir && (fid->dir.dir == fsi->root_dir) && (fid->entry == -1)) { + info->Attr = ATTR_SUBDIR; + memset((s8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); + memset((s8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); + memset((s8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + //strcpy(info->NameBuf.sfn, "."); + //strcpy(info->NameBuf.lfn, "."); + + dir.dir = fsi->root_dir; + dir.flags = 0x01; + dir.size = 0; /* UNUSED */ + + /* FAT16 root_dir */ + if (IS_CLUS_FREE(fsi->root_dir)) { + info->Size = fsi->dentries_in_root << DENTRY_SIZE_BITS; + } else { + u32 num_clu; + + if (__count_num_clusters(sb, &dir, &num_clu)) + return -EIO; + info->Size = (u64)num_clu << fsi->cluster_size_bits; + } + + count = __count_dos_name_entries(sb, &dir, TYPE_DIR, NULL); + if (count < 0) + return -EIO; + info->NumSubdirs = count; + + return 0; + } + + /* get the directory entry of given file or directory */ + if (fsi->vol_type == EXFAT) { + /* es should be released */ + es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + } else { + ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return -EIO; + ep2 = ep; + /* dcache should be unlocked */ + dcache_lock(sb, sector); + } + + /* set FILE_INFO structure using the acquired DENTRY_T */ + info->Attr = fsi->fs_func->get_entry_attr(ep); + + fsi->fs_func->get_entry_time(ep, &tm, TM_CREATE); + info->CreateTimestamp.Year = tm.year; + info->CreateTimestamp.Month = tm.mon; + info->CreateTimestamp.Day = tm.day; + info->CreateTimestamp.Hour = tm.hour; + info->CreateTimestamp.Minute = tm.min; + info->CreateTimestamp.Second = tm.sec; + info->CreateTimestamp.MilliSecond = 0; + + fsi->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + info->ModifyTimestamp.Year = tm.year; + info->ModifyTimestamp.Month = tm.mon; + info->ModifyTimestamp.Day = tm.day; + info->ModifyTimestamp.Hour = tm.hour; + info->ModifyTimestamp.Minute = tm.min; + info->ModifyTimestamp.Second = tm.sec; + info->ModifyTimestamp.MilliSecond = 0; + + memset((s8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + info->NumSubdirs = 0; + info->Size = fsi->fs_func->get_entry_size(ep2); + + if (fsi->vol_type == EXFAT) + release_dentry_set(es); + else + dcache_unlock(sb, sector); + + if (is_dir) { + u32 dotcnt = 0; + + dir.dir = fid->start_clu; + dir.flags = fid->flags; + dir.size = fid->size >> fsi->cluster_size_bits; + /* + * NOTE : + * If "dir.flags" has 0x01, "dir.size" is meaningless. + */ +#if 0 + if (info->Size == 0) { + s32 num_clu; + + if (__count_num_clusters(sb, &dir, &num_clu)) + return -EIO; + info->Size = (u64)num_clu << fsi->cluster_size_bits; + } +#endif + count = __count_dos_name_entries(sb, &dir, TYPE_DIR, &dotcnt); + if (count < 0) + return -EIO; + + if (fsi->vol_type == EXFAT) { + count += SDFAT_MIN_SUBDIR; + } else { + /* + * if directory has been corrupted, + * we have to adjust subdir count. + */ + BUG_ON(dotcnt > SDFAT_MIN_SUBDIR); + if (dotcnt < SDFAT_MIN_SUBDIR) { + EMSG("%s: contents of the directory has been " + "corrupted (parent clus : %08x, idx : %d)", + __func__, fid->dir.dir, fid->entry); + } + count += (SDFAT_MIN_SUBDIR - dotcnt); + } + info->NumSubdirs = count; + } + + TMSG("%s exited successfully\n", __func__); + return 0; +} /* end of fscore_read_inode */ + +/* set the information of a given file + * REMARK : This function does not need any file name on linux + */ +s32 fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, s32 sync) +{ + s32 ret = -EIO; + u64 sector; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + + /* SKIP WRITING INODE : + * if the indoe is already unlinked, + * there is no need for updating inode + */ + if (fid->dir.dir == DIR_DELETED) + return 0; + + if (is_dir && (fid->dir.dir == fsi->root_dir) && (fid->entry == -1)) + return 0; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* get the directory entry of given file or directory */ + if (fsi->vol_type == EXFAT) { + es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep+1; + } else { + /* for other than exfat */ + ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return -EIO; + ep2 = ep; + } + + + fsi->fs_func->set_entry_attr(ep, info->Attr); + + /* set FILE_INFO structure using the acquired DENTRY_T */ + tm.sec = info->CreateTimestamp.Second; + tm.min = info->CreateTimestamp.Minute; + tm.hour = info->CreateTimestamp.Hour; + tm.day = info->CreateTimestamp.Day; + tm.mon = info->CreateTimestamp.Month; + tm.year = info->CreateTimestamp.Year; + fsi->fs_func->set_entry_time(ep, &tm, TM_CREATE); + + tm.sec = info->ModifyTimestamp.Second; + tm.min = info->ModifyTimestamp.Minute; + tm.hour = info->ModifyTimestamp.Hour; + tm.day = info->ModifyTimestamp.Day; + tm.mon = info->ModifyTimestamp.Month; + tm.year = info->ModifyTimestamp.Year; + fsi->fs_func->set_entry_time(ep, &tm, TM_MODIFY); + + if (is_dir && fsi->vol_type != EXFAT) { + /* overwirte dirsize if FAT32 and dir size != 0 */ + if (fsi->fs_func->get_entry_size(ep2)) + fsi->fs_func->set_entry_size(ep2, 0); + } else { + /* File size should be zero if there is no cluster allocated */ + u64 on_disk_size = info->Size; + + if (IS_CLUS_EOF(fid->start_clu)) + on_disk_size = 0; + + fsi->fs_func->set_entry_size(ep2, on_disk_size); + } + + if (fsi->vol_type == EXFAT) { + ret = update_dir_chksum_with_entry_set(sb, es); + release_dentry_set(es); + } else { + ret = dcache_modify(sb, sector); + } + + fs_sync(sb, sync); + /* Comment below code to prevent super block update frequently */ + //fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} /* end of fscore_write_inode */ + + +/* + * Input: inode, (logical) clu_offset, target allocation area + * Output: errcode, cluster number + * *clu = (~0), if it's unable to allocate a new cluster + */ +s32 fscore_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest) +{ + s32 ret, modified = false; + u32 last_clu; + u64 sector; + CHAIN_T new_clu; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + u32 local_clu_offset = clu_offset; + s32 reserved_clusters = fsi->reserved_clusters; + u32 num_to_be_allocated = 0, num_clusters = 0; + + fid->rwoffset = (s64)(clu_offset) << fsi->cluster_size_bits; + + if (SDFAT_I(inode)->i_size_ondisk > 0) + num_clusters = (u32)((SDFAT_I(inode)->i_size_ondisk-1) >> fsi->cluster_size_bits) + 1; + + if (clu_offset >= num_clusters) + num_to_be_allocated = clu_offset - num_clusters + 1; + + if ((dest == ALLOC_NOWHERE) && (num_to_be_allocated > 0)) { + *clu = CLUS_EOF; + return 0; + } + + /* check always request cluster is 1 */ + //ASSERT(num_to_be_allocated == 1); + + sdfat_debug_check_clusters(inode); + + *clu = last_clu = fid->start_clu; + + /* XXX: Defensive code needed. + * what if i_size_ondisk != # of allocated clusters + */ + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (!IS_CLUS_EOF(*clu))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + *clu = CLUS_EOF; + else + *clu += clu_offset; + } + } else if (fid->type == TYPE_FILE) { + u32 fclus = 0; + s32 err = extent_get_clus(inode, clu_offset, + &fclus, clu, &last_clu, 1); + if (err) + return -EIO; + + clu_offset -= fclus; + } else { + /* hint information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + /* hint_bmap.clu should be valid */ + ASSERT(fid->hint_bmap.clu >= 2); + *clu = fid->hint_bmap.clu; + } + + while ((clu_offset > 0) && (!IS_CLUS_EOF(*clu))) { + last_clu = *clu; + if (get_next_clus_safe(sb, clu)) + return -EIO; + clu_offset--; + } + } + + if (IS_CLUS_EOF(*clu)) { + fs_set_vol_flags(sb, VOL_DIRTY); + + new_clu.dir = (IS_CLUS_EOF(last_clu)) ? CLUS_EOF : last_clu + 1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a cluster */ + if (num_to_be_allocated < 1) { + /* Broken FAT (i_sze > allocated FAT) */ + EMSG("%s: invalid fat chain : inode(%p) " + "num_to_be_allocated(%d) " + "i_size_ondisk(%lld) fid->flags(%02x) " + "fid->start(%08x) fid->hint_off(%u) " + "fid->hint_clu(%u) fid->rwoffset(%llu) " + "modified_clu_off(%d) last_clu(%08x) " + "new_clu(%08x)", __func__, inode, + num_to_be_allocated, + (SDFAT_I(inode)->i_size_ondisk), + fid->flags, fid->start_clu, + fid->hint_bmap.off, fid->hint_bmap.clu, + fid->rwoffset, clu_offset, + last_clu, new_clu.dir); + sdfat_fs_error(sb, "broken FAT chain."); + return -EIO; + } + + ret = fsi->fs_func->alloc_cluster(sb, num_to_be_allocated, &new_clu, ALLOC_COLD); + if (ret) + return ret; + + if (IS_CLUS_EOF(new_clu.dir) || IS_CLUS_FREE(new_clu.dir)) { + sdfat_fs_error(sb, "bogus cluster new allocated" + "(last_clu : %u, new_clu : %u)", + last_clu, new_clu.dir); + ASSERT(0); + return -EIO; + } + + /* Reserved cluster dec. */ + // XXX: Inode DA flag needed + if (SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_DELAY) { + BUG_ON(reserved_clusters < num_to_be_allocated); + reserved_clusters -= num_to_be_allocated; + + } + + /* (2) append to the FAT chain */ + if (IS_CLUS_EOF(last_clu)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = true; + } else { + if (new_clu.flags != fid->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with alloc-bmp + */ + chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = true; + } + if (new_clu.flags == 0x01) + if (fat_ent_set(sb, last_clu, new_clu.dir)) + return -EIO; + } + + num_clusters += num_to_be_allocated; + *clu = new_clu.dir; + + if (fid->dir.dir != DIR_DELETED) { + + if (fsi->vol_type == EXFAT) { + es = get_dentry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + /* get stream entry */ + ep++; + } + + /* (3) update directory entry */ + if (modified) { + if (fsi->vol_type != EXFAT) { + ep = get_dentry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return -EIO; + } + + if (fsi->fs_func->get_entry_flag(ep) != fid->flags) + fsi->fs_func->set_entry_flag(ep, fid->flags); + + if (fsi->fs_func->get_entry_clu0(ep) != fid->start_clu) + fsi->fs_func->set_entry_clu0(ep, fid->start_clu); + + fsi->fs_func->set_entry_size(ep, fid->size); + + if (fsi->vol_type != EXFAT) { + if (dcache_modify(sb, sector)) + return -EIO; + } + } + + if (fsi->vol_type == EXFAT) { + if (update_dir_chksum_with_entry_set(sb, es)) + return -EIO; + release_dentry_set(es); + } + + } /* end of if != DIR_DELETED */ + + + /* add number of new blocks to inode (non-DA only) */ + if (!(SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_DELAY)) { + inode->i_blocks += num_to_be_allocated << (fsi->cluster_size_bits - sb->s_blocksize_bits); + } else { + // DA의 경우, i_blocks가 이미 증가해있어야 함. + BUG_ON(clu_offset >= (inode->i_blocks >> (fsi->cluster_size_bits - sb->s_blocksize_bits))); + } +#if 0 + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + /* (4) Move *clu pointer along FAT chains (hole care) + * because the caller of this function expect *clu to be the last cluster. + * This only works when num_to_be_allocated >= 2, + * *clu = (the first cluster of the allocated chain) => (the last cluster of ...) + */ + if (fid->flags == 0x03) { + *clu += num_to_be_allocated - 1; + } else { + while (num_to_be_allocated > 1) { + if (get_next_clus_safe(sb, clu)) + return -EIO; + num_to_be_allocated--; + } + } + + } + + /* update reserved_clusters */ + fsi->reserved_clusters = reserved_clusters; + + /* hint information */ + fid->hint_bmap.off = local_clu_offset; + fid->hint_bmap.clu = *clu; + + return 0; +} /* end of fscore_map_clus */ + +/* allocate reserved cluster */ +s32 fscore_reserve_clus(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if ((fsi->used_clusters + fsi->reserved_clusters) >= (fsi->num_clusters - 2)) + return -ENOSPC; + + if (bdev_check_bdi_valid(sb)) + return -EIO; + + fsi->reserved_clusters++; + + /* inode->i_blocks update */ + inode->i_blocks += 1 << (fsi->cluster_size_bits - sb->s_blocksize_bits); + + sdfat_debug_check_clusters(inode); + + return 0; +} + +/* remove an entry, BUT don't truncate */ +s32 fscore_unlink(struct inode *inode, FILE_ID_T *fid) +{ + s32 dentry; + CHAIN_T dir; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + return -ENOENT; + } + + ep = get_dentry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return -EIO; + +#ifdef CONFIG_SDFAT_CHECK_RO_ATTR + if (SDFAT_SB(sb)->fsi.fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; +#endif + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + if (remove_file(inode, &dir, dentry)) + return -EIO; + + /* This doesn't modify fid */ + fid->dir.dir = DIR_DELETED; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return 0; +} + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* create a directory */ +s32 fscore_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + + TMSG("%s entered\n", __func__); + + /* check the validity of directory name in the given old pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + goto out; + + fs_set_vol_flags(sb, VOL_DIRTY); + + ret = create_dir(inode, &dir, &uni_name, fid); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +out: + TMSG("%s exited with err(%d)\n", __func__, ret); + return ret; +} + +/* read a directory entry from the opened directory */ +s32 fscore_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + s32 i; + s32 dentries_per_clu, dentries_per_clu_bits = 0; + u32 type, clu_offset; + u64 sector; + CHAIN_T dir, clu; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + u32 dentry = (u32)(fid->rwoffset & 0xFFFFFFFF); /* u32 is enough for directory */ + + /* check if the given file ID is opened */ + if (fid->type != TYPE_DIR) + return -EPERM; + + if (fid->entry == -1) { + dir.dir = fsi->root_dir; + dir.size = 0; /* just initialize, but will not use */ + dir.flags = 0x01; + } else { + dir.dir = fid->start_clu; + dir.size = fid->size >> fsi->cluster_size_bits; + dir.flags = fid->flags; + sdfat_debug_bug_on(dentry >= (dir.size * fsi->dentries_per_clu)); + } + + if (IS_CLUS_FREE(dir.dir)) { /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + + /* Prevent readdir over directory size */ + if (dentry >= dentries_per_clu) { + clu.dir = CLUS_EOF; + } else { + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + } + } else { + dentries_per_clu = fsi->dentries_per_clu; + dentries_per_clu_bits = ilog2(dentries_per_clu); + + clu_offset = dentry >> dentries_per_clu_bits; + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + + if (clu.flags == 0x03) { + clu.dir += clu_offset; + clu.size -= clu_offset; + } else { + /* hint_information */ + if ((clu_offset > 0) && + ((fid->hint_bmap.off != CLUS_EOF) && (fid->hint_bmap.off > 0)) && + (clu_offset >= fid->hint_bmap.off)) { + clu_offset -= fid->hint_bmap.off; + clu.dir = fid->hint_bmap.clu; + } + + while (clu_offset > 0) { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + + clu_offset--; + } + } + } + + while (!IS_CLUS_EOF(clu.dir)) { + if (IS_CLUS_FREE(dir.dir)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = get_dentry_in_dir(sb, &clu, i, §or); + if (!ep) + return -EIO; + + type = fsi->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + break; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + dcache_lock(sb, sector); + dir_entry->Attr = fsi->fs_func->get_entry_attr(ep); + + fsi->fs_func->get_entry_time(ep, &tm, TM_CREATE); + dir_entry->CreateTimestamp.Year = tm.year; + dir_entry->CreateTimestamp.Month = tm.mon; + dir_entry->CreateTimestamp.Day = tm.day; + dir_entry->CreateTimestamp.Hour = tm.hour; + dir_entry->CreateTimestamp.Minute = tm.min; + dir_entry->CreateTimestamp.Second = tm.sec; + dir_entry->CreateTimestamp.MilliSecond = 0; + + fsi->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + dir_entry->ModifyTimestamp.Year = tm.year; + dir_entry->ModifyTimestamp.Month = tm.mon; + dir_entry->ModifyTimestamp.Day = tm.day; + dir_entry->ModifyTimestamp.Hour = tm.hour; + dir_entry->ModifyTimestamp.Minute = tm.min; + dir_entry->ModifyTimestamp.Second = tm.sec; + dir_entry->ModifyTimestamp.MilliSecond = 0; + + memset((s8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + fsi->fs_func->get_uniname_from_ext_entry(sb, &dir, dentry, uni_name.name); + if (*(uni_name.name) == 0x0) + get_uniname_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + nls_uni16s_to_vfsname(sb, &uni_name, + dir_entry->NameBuf.lfn, + dir_entry->NameBuf.lfnbuf_len); + dcache_unlock(sb, sector); + + if (fsi->vol_type == EXFAT) { + ep = get_dentry_in_dir(sb, &clu, i+1, NULL); + if (!ep) + return -EIO; + } else { + get_uniname_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); + nls_uni16s_to_vfsname(sb, &uni_name, + dir_entry->NameBuf.sfn, + dir_entry->NameBuf.sfnbuf_len); + } + + dir_entry->Size = fsi->fs_func->get_entry_size(ep); + + /* + * Update hint information : + * fat16 root directory does not need it. + */ + if (!IS_CLUS_FREE(dir.dir)) { + fid->hint_bmap.off = dentry >> dentries_per_clu_bits; + fid->hint_bmap.clu = clu.dir; + } + + fid->rwoffset = (s64) ++dentry; + + return 0; + } + + /* fat16 root directory */ + if (IS_CLUS_FREE(dir.dir)) + break; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &(clu.dir))) + return -EIO; + } + } + + dir_entry->NameBuf.lfn[0] = '\0'; + + fid->rwoffset = (s64)dentry; + + return 0; +} /* end of fscore_readdir */ + +/* remove a directory */ +s32 fscore_rmdir(struct inode *inode, FILE_ID_T *fid) +{ + s32 ret; + s32 dentry; + DENTRY_T *ep; + CHAIN_T dir, clu_to_free; + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + if (fid->dir.dir == DIR_DELETED) { + EMSG("%s : abnormal access to deleted dentry\n", __func__); + return -ENOENT; + } + + /* check if the file is "." or ".." */ + if (fsi->vol_type != EXFAT) { + if ((dir.dir != fsi->root_dir) && (dentry < 2)) + return -EPERM; + } + + ep = get_dentry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return -EIO; + +#ifdef CONFIG_SDFAT_CHECK_RO_ATTR + if (SDFAT_SB(sb)->fsi.fs_func->get_entry_attr(ep) & ATTR_READONLY) + return -EPERM; +#endif + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = ((fid->size-1) >> fsi->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + ret = check_dir_empty(sb, &clu_to_free); + if (ret) { + if (ret == -EIO) + EMSG("%s : failed to check_dir_empty : err(%d)\n", + __func__, ret); + return ret; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + ret = remove_file(inode, &dir, dentry); + if (ret) { + EMSG("%s : failed to remove_file : err(%d)\n", __func__, ret); + return ret; + } + + fid->dir.dir = DIR_DELETED; + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + return ret; +} /* end of fscore_rmdir */ + +/* end of core.c */ diff --git a/fs/sdfat/core.h b/fs/sdfat/core.h new file mode 100644 index 0000000000000..00ea2c5e26032 --- /dev/null +++ b/fs/sdfat/core.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _SDFAT_CORE_H +#define _SDFAT_CORE_H + +#include + +#include "config.h" +#include "api.h" +#include "upcase.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ +#define get_next_clus(sb, pclu) fat_ent_get(sb, *(pclu), pclu) +#define get_next_clus_safe(sb, pclu) fat_ent_get_safe(sb, *(pclu), pclu) + +/* file status */ +/* this prevents + * fscore_write_inode, fscore_map_clus, ... with the unlinked inodes + * from corrupting on-disk dentry data. + * + * The fid->dir value of unlinked inode will be DIR_DELETED + * and those functions must check if fid->dir is valid prior to + * the calling of get_dentry_in_dir() + */ +#define DIR_DELETED 0xFFFF0321 + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ +#define ES_2_ENTRIES 2 +#define ES_3_ENTRIES 3 +#define ES_ALL_ENTRIES 0 + +typedef struct { + u64 sector; // sector number that contains file_entry + u32 offset; // byte offset in the sector + s32 alloc_flag; // flag in stream entry. 01 for cluster chain, 03 for contig. clusters. + u32 num_entries; + void *__buf; // __buf should be the last member +} ENTRY_SET_CACHE_T; + + + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* file system initialization & shutdown functions */ +s32 fscore_init(void); +s32 fscore_shutdown(void); + +/* chain management */ +s32 chain_cont_cluster(struct super_block *sb, u32 chain, u32 len); + +/* volume management functions */ +s32 fscore_mount(struct super_block *sb); +s32 fscore_umount(struct super_block *sb); +s32 fscore_statfs(struct super_block *sb, VOL_INFO_T *info); +s32 fscore_sync_fs(struct super_block *sb, s32 do_sync); +s32 fscore_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync); +u32 fscore_get_au_stat(struct super_block *sb, s32 mode); + +/* file management functions */ +s32 fscore_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid); +s32 fscore_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid); +s32 fscore_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); +s32 fscore_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); +s32 fscore_truncate(struct inode *inode, u64 old_size, u64 new_size); +s32 fscore_rename(struct inode *old_parent_inode, FILE_ID_T *fid, + struct inode *new_parent_inode, struct dentry *new_dentry); +s32 fscore_remove(struct inode *inode, FILE_ID_T *fid); +s32 fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info); +s32 fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync); +s32 fscore_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest); +s32 fscore_reserve_clus(struct inode *inode); +s32 fscore_unlink(struct inode *inode, FILE_ID_T *fid); + +/* directory management functions */ +s32 fscore_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid); +s32 fscore_readdir(struct inode *inode, DIR_ENTRY_T *dir_ent); +s32 fscore_rmdir(struct inode *inode, FILE_ID_T *fid); + + +/*----------------------------------------------------------------------*/ +/* External Function Declarations (NOT TO UPPER LAYER) */ +/*----------------------------------------------------------------------*/ + +/* core.c : core code for common */ +/* dir entry management functions */ +DENTRY_T *get_dentry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u64 *sector); + +/* name conversion functions */ +void get_uniname_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode); + +/* file operation functions */ +s32 walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, u32 byte_offset, u32 *clu); + +/* sdfat/cache.c */ +s32 meta_cache_init(struct super_block *sb); +s32 meta_cache_shutdown(struct super_block *sb); +u8 *fcache_getblk(struct super_block *sb, u64 sec); +s32 fcache_modify(struct super_block *sb, u64 sec); +s32 fcache_release_all(struct super_block *sb); +s32 fcache_flush(struct super_block *sb, u32 sync); + +u8 *dcache_getblk(struct super_block *sb, u64 sec); +s32 dcache_modify(struct super_block *sb, u64 sec); +s32 dcache_lock(struct super_block *sb, u64 sec); +s32 dcache_unlock(struct super_block *sb, u64 sec); +s32 dcache_release(struct super_block *sb, u64 sec); +s32 dcache_release_all(struct super_block *sb); +s32 dcache_flush(struct super_block *sb, u32 sync); +s32 dcache_readahead(struct super_block *sb, u64 sec); + + +/* fatent.c */ +s32 fat_ent_ops_init(struct super_block *sb); +s32 fat_ent_get(struct super_block *sb, u32 loc, u32 *content); +s32 fat_ent_set(struct super_block *sb, u32 loc, u32 content); +s32 fat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content); + +/* core_fat.c : core code for fat */ +s32 fat_generate_dos_name_new(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname, s32 n_entries); +s32 mount_fat16(struct super_block *sb, pbr_t *p_pbr); +s32 mount_fat32(struct super_block *sb, pbr_t *p_pbr); + +/* core_exfat.c : core code for exfat */ + +s32 load_alloc_bmp(struct super_block *sb); +void free_alloc_bmp(struct super_block *sb); +ENTRY_SET_CACHE_T *get_dentry_set_in_dir(struct super_block *sb, + CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep); +void release_dentry_set(ENTRY_SET_CACHE_T *es); +s32 update_dir_chksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry); +s32 update_dir_chksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es); +bool is_dir_empty(struct super_block *sb, CHAIN_T *p_dir); +s32 mount_exfat(struct super_block *sb, pbr_t *p_pbr); + +/* amap_smart.c : creation on mount / destroy on umount */ +int amap_create(struct super_block *sb, u32 pack_ratio, u32 sect_per_au, u32 hidden_sect); +void amap_destroy(struct super_block *sb); + +/* amap_smart.c : (de)allocation functions */ +s32 amap_fat_alloc_cluster(struct super_block *sb, u32 num_alloc, CHAIN_T *p_chain, s32 dest); +s32 amap_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse);/* Not impelmented */ +s32 amap_release_cluster(struct super_block *sb, u32 clu); /* Only update AMAP */ + +/* amap_smart.c : misc (for defrag) */ +s32 amap_mark_ignore(struct super_block *sb, u32 clu); +s32 amap_unmark_ignore(struct super_block *sb, u32 clu); +s32 amap_unmark_ignore_all(struct super_block *sb); +s32 amap_check_working(struct super_block *sb, u32 clu); +s32 amap_get_freeclus(struct super_block *sb, u32 clu); + +/* amap_smart.c : stat AU */ +u32 amap_get_au_stat(struct super_block *sb, s32 mode); + + +/* blkdev.c */ +s32 bdev_open_dev(struct super_block *sb); +s32 bdev_close_dev(struct super_block *sb); +s32 bdev_check_bdi_valid(struct super_block *sb); +s32 bdev_readahead(struct super_block *sb, u64 secno, u64 num_secs); +s32 bdev_mread(struct super_block *sb, u64 secno, struct buffer_head **bh, u64 num_secs, s32 read); +s32 bdev_mwrite(struct super_block *sb, u64 secno, struct buffer_head *bh, u64 num_secs, s32 sync); +s32 bdev_sync_all(struct super_block *sb); + +/* blkdev.c : sector read/write functions */ +s32 read_sect(struct super_block *sb, u64 sec, struct buffer_head **bh, s32 read); +s32 write_sect(struct super_block *sb, u64 sec, struct buffer_head *bh, s32 sync); +s32 read_msect(struct super_block *sb, u64 sec, struct buffer_head **bh, s64 num_secs, s32 read); +s32 write_msect(struct super_block *sb, u64 sec, struct buffer_head *bh, s64 num_secs, s32 sync); +s32 write_msect_zero(struct super_block *sb, u64 sec, u64 num_secs); + +/* misc.c */ +u8 calc_chksum_1byte(void *data, s32 len, u8 chksum); +u16 calc_chksum_2byte(void *data, s32 len, u16 chksum, s32 type); + +/* extent.c */ +s32 extent_cache_init(void); +void extent_cache_shutdown(void); +void extent_cache_init_inode(struct inode *inode); +void extent_cache_inval_inode(struct inode *inode); +s32 extent_get_clus(struct inode *inode, u32 cluster, u32 *fclus, + u32 *dclus, u32 *last_dclus, s32 allow_eof); +/*----------------------------------------------------------------------*/ +/* Wrapper Function */ +/*----------------------------------------------------------------------*/ +void set_sb_dirty(struct super_block *sb); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _SDFAT_CORE_H */ + +/* end of core.h */ diff --git a/fs/sdfat/core_exfat.c b/fs/sdfat/core_exfat.c new file mode 100644 index 0000000000000..08c45b09bb78c --- /dev/null +++ b/fs/sdfat/core_exfat.c @@ -0,0 +1,1554 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : core_exfat.c */ +/* PURPOSE : exFAT-fs core code for sdFAT */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include +#include + +#include "sdfat.h" +#include "core.h" +#include +#include + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ +static u8 free_bit[] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/ + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/ + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/ + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/ + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/ +}; + +static u8 used_bit[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/ + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/ + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/ + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/ + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/ + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/ + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/ + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/ + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/ + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/ +}; + + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ +/* + * Directory Entry Management Functions + */ +static u32 exfat_get_entry_type(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (ep->type == EXFAT_UNUSED) + return TYPE_UNUSED; + if (ep->type < 0x80) + return TYPE_DELETED; + if (ep->type == 0x80) + return TYPE_INVALID; + if (ep->type < 0xA0) { + if (ep->type == 0x81) + return TYPE_BITMAP; + if (ep->type == 0x82) + return TYPE_UPCASE; + if (ep->type == 0x83) + return TYPE_VOLUME; + if (ep->type == 0x85) { + if (le16_to_cpu(ep->attr) & ATTR_SUBDIR) + return TYPE_DIR; + return TYPE_FILE; + } + return TYPE_CRITICAL_PRI; + } + if (ep->type < 0xC0) { + if (ep->type == 0xA0) + return TYPE_GUID; + if (ep->type == 0xA1) + return TYPE_PADDING; + if (ep->type == 0xA2) + return TYPE_ACLTAB; + return TYPE_BENIGN_PRI; + } + if (ep->type < 0xE0) { + if (ep->type == 0xC0) + return TYPE_STREAM; + if (ep->type == 0xC1) + return TYPE_EXTEND; + if (ep->type == 0xC2) + return TYPE_ACL; + return TYPE_CRITICAL_SEC; + } + return TYPE_BENIGN_SEC; +} /* end of exfat_get_entry_type */ + +static void exfat_set_entry_type(DENTRY_T *p_entry, u32 type) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) { + ep->type = 0x0; + } else if (type == TYPE_DELETED) { + ep->type &= ~0x80; + } else if (type == TYPE_STREAM) { + ep->type = 0xC0; + } else if (type == TYPE_EXTEND) { + ep->type = 0xC1; + } else if (type == TYPE_BITMAP) { + ep->type = 0x81; + } else if (type == TYPE_UPCASE) { + ep->type = 0x82; + } else if (type == TYPE_VOLUME) { + ep->type = 0x83; + } else if (type == TYPE_DIR) { + ep->type = 0x85; + ep->attr = cpu_to_le16(ATTR_SUBDIR); + } else if (type == TYPE_FILE) { + ep->type = 0x85; + ep->attr = cpu_to_le16(ATTR_ARCHIVE); + } else if (type == TYPE_SYMLINK) { + ep->type = 0x85; + ep->attr = cpu_to_le16(ATTR_ARCHIVE | ATTR_SYMLINK); + } +} /* end of exfat_set_entry_type */ + +static u32 exfat_get_entry_attr(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + return (u32)le16_to_cpu(ep->attr); +} /* end of exfat_get_entry_attr */ + +static void exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + ep->attr = cpu_to_le16((u16) attr); +} /* end of exfat_set_entry_attr */ + +static u8 exfat_get_entry_flag(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + return ep->flags; +} /* end of exfat_get_entry_flag */ + +static void exfat_set_entry_flag(DENTRY_T *p_entry, u8 flags) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + ep->flags = flags; +} /* end of exfat_set_entry_flag */ + +static u32 exfat_get_entry_clu0(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + return (u32)le32_to_cpu(ep->start_clu); +} /* end of exfat_get_entry_clu0 */ + +static void exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + ep->start_clu = cpu_to_le32(start_clu); +} /* end of exfat_set_entry_clu0 */ + +static u64 exfat_get_entry_size(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + return le64_to_cpu(ep->valid_size); +} /* end of exfat_get_entry_size */ + +static void exfat_set_entry_size(DENTRY_T *p_entry, u64 size) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *)p_entry; + + ep->valid_size = cpu_to_le64(size); + ep->size = cpu_to_le64(size); +} /* end of exfat_set_entry_size */ + +static void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t = 0x00, d = 0x21; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + switch (mode) { + case TM_CREATE: + t = le16_to_cpu(ep->create_time); + d = le16_to_cpu(ep->create_date); + break; + case TM_MODIFY: + t = le16_to_cpu(ep->modify_time); + d = le16_to_cpu(ep->modify_date); + break; + case TM_ACCESS: + t = le16_to_cpu(ep->access_time); + d = le16_to_cpu(ep->access_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of exfat_get_entry_time */ + +static void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t, d; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + ep->create_time = cpu_to_le16(t); + ep->create_date = cpu_to_le16(d); + break; + case TM_MODIFY: + ep->modify_time = cpu_to_le16(t); + ep->modify_date = cpu_to_le16(d); + break; + case TM_ACCESS: + ep->access_time = cpu_to_le16(t); + ep->access_date = cpu_to_le16(d); + break; + } +} /* end of exfat_set_entry_time */ + + +static void __init_file_entry(struct super_block *sb, FILE_DENTRY_T *ep, u32 type) +{ + TIMESTAMP_T tm, *tp; + + exfat_set_entry_type((DENTRY_T *) ep, type); + + tp = tm_now(SDFAT_SB(sb), &tm); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); + ep->create_time_ms = 0; + ep->modify_time_ms = 0; + ep->access_time_ms = 0; +} /* end of __init_file_entry */ + +static void __init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size) +{ + exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM); + ep->flags = flags; + ep->start_clu = cpu_to_le32(start_clu); + ep->valid_size = cpu_to_le64(size); + ep->size = cpu_to_le64(size); +} /* end of __init_strm_entry */ + +static void __init_name_entry(NAME_DENTRY_T *ep, u16 *uniname) +{ + s32 i; + + exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->flags = 0x0; + + for (i = 0; i < 15; i++) { + ep->unicode_0_14[i] = cpu_to_le16(*uniname); + if (*uniname == 0x0) + break; + uniname++; + } +} /* end of __init_name_entry */ + +static s32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size) +{ + u64 sector; + u8 flags; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + + flags = (type == TYPE_FILE) ? 0x01 : 0x03; + + /* we cannot use get_dentry_set_in_dir here because file ep is not initialized yet */ + file_ep = (FILE_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return -EIO; + + strm_ep = (STRM_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return -EIO; + + __init_file_entry(sb, file_ep, type); + if (dcache_modify(sb, sector)) + return -EIO; + + __init_strm_entry(strm_ep, flags, start_clu, size); + if (dcache_modify(sb, sector)) + return -EIO; + + return 0; +} /* end of exfat_init_dir_entry */ + +s32 update_dir_chksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry) +{ + s32 ret = -EIO; + s32 i, num_entries; + u64 sector; + u16 chksum; + FILE_DENTRY_T *file_ep; + DENTRY_T *ep; + + file_ep = (FILE_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return -EIO; + + dcache_lock(sb, sector); + + num_entries = (s32) file_ep->num_ext + 1; + chksum = calc_chksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY); + + for (i = 1; i < num_entries; i++) { + ep = get_dentry_in_dir(sb, p_dir, entry+i, NULL); + if (!ep) + goto out_unlock; + + chksum = calc_chksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); + } + + file_ep->checksum = cpu_to_le16(chksum); + ret = dcache_modify(sb, sector); +out_unlock: + dcache_unlock(sb, sector); + return ret; + +} /* end of update_dir_chksum */ + + +static s32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + s32 i; + u64 sector; + u16 *uniname = p_uniname->name; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + + file_ep = (FILE_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return -EIO; + + file_ep->num_ext = (u8)(num_entries - 1); + dcache_modify(sb, sector); + + strm_ep = (STRM_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return -EIO; + + strm_ep->name_len = p_uniname->name_len; + strm_ep->name_hash = cpu_to_le16(p_uniname->name_hash); + dcache_modify(sb, sector); + + for (i = 2; i < num_entries; i++) { + name_ep = (NAME_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry+i, §or); + if (!name_ep) + return -EIO; + + __init_name_entry(name_ep, uniname); + dcache_modify(sb, sector); + uniname += 15; + } + + update_dir_chksum(sb, p_dir, entry); + + return 0; +} /* end of exfat_init_ext_entry */ + + +static s32 exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries) +{ + s32 i; + u64 sector; + DENTRY_T *ep; + + for (i = order; i < num_entries; i++) { + ep = get_dentry_in_dir(sb, p_dir, entry+i, §or); + if (!ep) + return -EIO; + + exfat_set_entry_type(ep, TYPE_DELETED); + if (dcache_modify(sb, sector)) + return -EIO; + } + + return 0; +} + +static s32 __write_partial_entries_in_entry_set(struct super_block *sb, + ENTRY_SET_CACHE_T *es, u64 sec, u32 off, u32 count) +{ + s32 num_entries; + u32 buf_off = (off - es->offset); + u32 remaining_byte_in_sector, copy_entries; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u32 clu; + u8 *buf, *esbuf = (u8 *)&(es->__buf); + + TMSG("%s entered\n", __func__); + MMSG("%s: es %p sec %llu off %u cnt %d\n", __func__, es, sec, off, count); + num_entries = count; + + while (num_entries) { + /* write per sector base */ + remaining_byte_in_sector = (1 << sb->s_blocksize_bits) - off; + copy_entries = min((s32)(remaining_byte_in_sector >> DENTRY_SIZE_BITS), num_entries); + buf = dcache_getblk(sb, sec); + if (!buf) + goto err_out; + MMSG("es->buf %p buf_off %u\n", esbuf, buf_off); + MMSG("copying %d entries from %p to sector %llu\n", copy_entries, (esbuf + buf_off), sec); + memcpy(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); + dcache_modify(sb, sec); + num_entries -= copy_entries; + + if (num_entries) { + // get next sector + if (IS_LAST_SECT_IN_CLUS(fsi, sec)) { + clu = SECT_TO_CLUS(fsi, sec); + if (es->alloc_flag == 0x03) + clu++; + else if (get_next_clus_safe(sb, &clu)) + goto err_out; + sec = CLUS_TO_SECT(fsi, clu); + } else { + sec++; + } + off = 0; + buf_off += copy_entries << DENTRY_SIZE_BITS; + } + } + + TMSG("%s exited successfully\n", __func__); + return 0; +err_out: + TMSG("%s failed\n", __func__); + return -EIO; +} + +/* write back all entries in entry set */ +static s32 __write_whole_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + return __write_partial_entries_in_entry_set(sb, es, es->sector, es->offset, es->num_entries); +} + +s32 update_dir_chksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + DENTRY_T *ep; + u16 chksum = 0; + s32 chksum_type = CS_DIR_ENTRY, i; + + ep = (DENTRY_T *)&(es->__buf); + for (i = 0; i < es->num_entries; i++) { + MMSG("%s %p\n", __func__, ep); + chksum = calc_chksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); + ep++; + chksum_type = CS_DEFAULT; + } + + ep = (DENTRY_T *)&(es->__buf); + ((FILE_DENTRY_T *)ep)->checksum = cpu_to_le16(chksum); + return __write_whole_entry_set(sb, es); +} + +/* returns a set of dentries for a file or dir. + * Note that this is a copy (dump) of dentries so that user should call write_entry_set() + * to apply changes made in this entry set to the real device. + * in: + * sb+p_dir+entry: indicates a file/dir + * type: specifies how many dentries should be included. + * out: + * file_ep: will point the first dentry(= file dentry) on success + * return: + * pointer of entry set on success, + * NULL on failure. + */ + +#define ES_MODE_STARTED 0 +#define ES_MODE_GET_FILE_ENTRY 1 +#define ES_MODE_GET_STRM_ENTRY 2 +#define ES_MODE_GET_NAME_ENTRY 3 +#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4 +ENTRY_SET_CACHE_T *get_dentry_set_in_dir(struct super_block *sb, + CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep) +{ + s32 ret; + u32 off, byte_offset, clu = 0; + u32 entry_type; + u64 sec; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + ENTRY_SET_CACHE_T *es = NULL; + DENTRY_T *ep, *pos; + u8 *buf; + u8 num_entries; + s32 mode = ES_MODE_STARTED; + + /* FIXME : is available in error case? */ + if (p_dir->dir == DIR_DELETED) { + EMSG("%s : access to deleted dentry\n", __func__); + BUG_ON(!fsi->prev_eio); + return NULL; + } + + TMSG("%s entered\n", __func__); + MMSG("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size); + MMSG("entry %d type %d\n", entry, type); + + byte_offset = entry << DENTRY_SIZE_BITS; + ret = walk_fat_chain(sb, p_dir, byte_offset, &clu); + if (ret) + return NULL; + + /* byte offset in cluster */ + byte_offset &= fsi->cluster_size - 1; + + /* byte offset in sector */ + off = byte_offset & (u32)(sb->s_blocksize - 1); + + /* sector offset in cluster */ + sec = byte_offset >> (sb->s_blocksize_bits); + sec += CLUS_TO_SECT(fsi, clu); + + buf = dcache_getblk(sb, sec); + if (!buf) + goto err_out; + + ep = (DENTRY_T *)(buf + off); + entry_type = exfat_get_entry_type(ep); + + if ((entry_type != TYPE_FILE) + && (entry_type != TYPE_DIR)) + goto err_out; + + if (type == ES_ALL_ENTRIES) + num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1; + else + num_entries = type; + + MMSG("trying to malloc %lx bytes for %d entries\n", + (unsigned long)(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)), num_entries); + es = kmalloc((offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)), GFP_KERNEL); + if (!es) { + EMSG("%s: failed to alloc entryset\n", __func__); + goto err_out; + } + + es->num_entries = num_entries; + es->sector = sec; + es->offset = off; + es->alloc_flag = p_dir->flags; + + pos = (DENTRY_T *) &(es->__buf); + + while (num_entries) { + // instead of copying whole sector, we will check every entry. + // this will provide minimum stablity and consistency. + entry_type = exfat_get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) + goto err_out; + + switch (mode) { + case ES_MODE_STARTED: + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) + mode = ES_MODE_GET_FILE_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_FILE_ENTRY: + if (entry_type == TYPE_STREAM) + mode = ES_MODE_GET_STRM_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_STRM_ENTRY: + if (entry_type == TYPE_EXTEND) + mode = ES_MODE_GET_NAME_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_NAME_ENTRY: + if (entry_type == TYPE_EXTEND) + break; + else if (entry_type == TYPE_STREAM) + goto err_out; + else if (entry_type & TYPE_CRITICAL_SEC) + mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_CRITICAL_SEC_ENTRY: + if ((entry_type == TYPE_EXTEND) || (entry_type == TYPE_STREAM)) + goto err_out; + else if ((entry_type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) + goto err_out; + break; + } + + /* copy dentry */ + memcpy(pos, ep, sizeof(DENTRY_T)); + + if (--num_entries == 0) + break; + + if (((off + DENTRY_SIZE) & (u32)(sb->s_blocksize - 1)) < + (off & (u32)(sb->s_blocksize - 1))) { + // get the next sector + if (IS_LAST_SECT_IN_CLUS(fsi, sec)) { + if (es->alloc_flag == 0x03) + clu++; + else if (get_next_clus_safe(sb, &clu)) + goto err_out; + sec = CLUS_TO_SECT(fsi, clu); + } else { + sec++; + } + buf = dcache_getblk(sb, sec); + if (!buf) + goto err_out; + off = 0; + ep = (DENTRY_T *)(buf); + } else { + ep++; + off += DENTRY_SIZE; + } + pos++; + } + + if (file_ep) + *file_ep = (DENTRY_T *)&(es->__buf); + + MMSG("es sec %llu offset %u flags %d, num_entries %u buf ptr %p\n", + es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); + TMSG("%s exited %p\n", __func__, es); + return es; +err_out: + TMSG("%s exited (return NULL) (es %p)\n", __func__, es); + + /* kfree(NULL) is safe */ + kfree(es); + es = NULL; + return NULL; +} + +void release_dentry_set(ENTRY_SET_CACHE_T *es) +{ + TMSG("%s %p\n", __func__, es); + + /* kfree(NULL) is safe */ + kfree(es); + es = NULL; +} + +static s32 __extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order) +{ + s32 i, len = 0; + + for (i = 0; i < 15; i++) { + /* FIXME : unaligned? */ + *uniname = le16_to_cpu(ep->unicode_0_14[i]); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} /* end of __extract_uni_name_from_name_entry */ + +#define DIRENT_STEP_FILE (0) +#define DIRENT_STEP_STRM (1) +#define DIRENT_STEP_NAME (2) +#define DIRENT_STEP_SECD (3) + +/* return values of exfat_find_dir_entry() + * >= 0 : return dir entiry position with the name in dir + * -EEXIST : (root dir, ".") it is the root dir itself + * -ENOENT : entry with the name does not exist + * -EIO : I/O error + */ +static s32 exfat_find_dir_entry(struct super_block *sb, FILE_ID_T *fid, + CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *unused, u32 type) +{ + s32 i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len; + s32 order, step, name_len; + s32 dentries_per_clu, num_empty = 0; + u32 entry_type; + u16 entry_uniname[16], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + HINT_T *hint_stat = &fid->hint_stat; + HINT_FEMP_T candi_empty; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* + * REMARK: + * DOT and DOTDOT are handled by VFS layer + */ + + if (IS_CLUS_FREE(p_dir->dir)) + return -EIO; + + dentries_per_clu = fsi->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + if (hint_stat->eidx) { + clu.dir = hint_stat->clu; + dentry = hint_stat->eidx; + end_eidx = dentry; + } + + candi_empty.eidx = -1; +rewind: + order = 0; + step = DIRENT_STEP_FILE; + while (!IS_CLUS_EOF(clu.dir)) { + i = dentry & (dentries_per_clu - 1); + for (; i < dentries_per_clu; i++, dentry++) { + if (rewind && (dentry == end_eidx)) + goto not_found; + + ep = get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + entry_type = exfat_get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { + step = DIRENT_STEP_FILE; + + num_empty++; + if (candi_empty.eidx == -1) { + if (num_empty == 1) { + candi_empty.cur.dir = clu.dir; + candi_empty.cur.size = clu.size; + candi_empty.cur.flags = clu.flags; + } + + if (num_empty >= num_entries) { + candi_empty.eidx = dentry - (num_empty - 1); + ASSERT(0 <= candi_empty.eidx); + candi_empty.count = num_empty; + + if ((fid->hint_femp.eidx == -1) || + (candi_empty.eidx <= fid->hint_femp.eidx)) { + memcpy(&fid->hint_femp, + &candi_empty, + sizeof(HINT_FEMP_T)); + } + } + } + + if (entry_type == TYPE_UNUSED) + goto not_found; + continue; + } + + num_empty = 0; + candi_empty.eidx = -1; + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + step = DIRENT_STEP_FILE; + if ((type == TYPE_ALL) || (type == entry_type)) { + file_ep = (FILE_DENTRY_T *) ep; + num_ext = file_ep->num_ext; + step = DIRENT_STEP_STRM; + } + continue; + } + + if (entry_type == TYPE_STREAM) { + if (step != DIRENT_STEP_STRM) { + step = DIRENT_STEP_FILE; + continue; + } + step = DIRENT_STEP_FILE; + strm_ep = (STRM_DENTRY_T *) ep; + if ((p_uniname->name_hash == le16_to_cpu(strm_ep->name_hash)) && + (p_uniname->name_len == strm_ep->name_len)) { + step = DIRENT_STEP_NAME; + order = 1; + name_len = 0; + } + continue; + } + + if (entry_type == TYPE_EXTEND) { + if (step != DIRENT_STEP_NAME) { + step = DIRENT_STEP_FILE; + continue; + } + name_ep = (NAME_DENTRY_T *) ep; + + if ((++order) == 2) + uniname = p_uniname->name; + else + uniname += 15; + + len = __extract_uni_name_from_name_entry(name_ep, entry_uniname, order); + name_len += len; + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (nls_cmp_uniname(sb, uniname, entry_uniname)) { + step = DIRENT_STEP_FILE; + } else if (name_len == p_uniname->name_len) { + if (order == num_ext) { + //fid->hint_femp.eidx = -1; + goto found; + } + step = DIRENT_STEP_SECD; + } + + *(uniname+len) = unichar; + continue; + } + + if (entry_type & (TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) { + if (step == DIRENT_STEP_SECD) { + if (++order == num_ext) + goto found; + continue; + } + } + step = DIRENT_STEP_FILE; + } + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + if (get_next_clus_safe(sb, &clu.dir)) + return -EIO; + } + } + +not_found: + /* we started at not 0 index,so we should try to find target + * from 0 index to the index we started at. + */ + if (!rewind && end_eidx) { + rewind = 1; + dentry = 0; + clu.dir = p_dir->dir; + /* reset empty hint */ + num_empty = 0; + candi_empty.eidx = -1; + goto rewind; + } + + /* initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + return -ENOENT; + +found: + /* next dentry we'll find is out of this cluster */ + if (!((dentry + 1) & (dentries_per_clu-1))) { + int ret = 0; + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUS_EOF; + } else { + ret = get_next_clus_safe(sb, &clu.dir); + } + + if (ret || IS_CLUS_EOF(clu.dir)) { + /* just initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + return (dentry - num_ext); + } + } + + hint_stat->clu = clu.dir; + hint_stat->eidx = dentry + 1; + return (dentry - num_ext); +} /* end of exfat_find_dir_entry */ + +/* returns -EIO on error */ +static s32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry) +{ + s32 i, count = 0; + u32 type; + FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; + DENTRY_T *ext_ep; + + for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) { + ext_ep = get_dentry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -EIO; + + type = exfat_get_entry_type(ext_ep); + if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) + count++; + else + return count; + } + + return count; +} /* end of exfat_count_ext_entries */ + + +/* + * Name Conversion Functions + */ +static void exfat_get_uniname_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname) +{ + s32 i; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es; + + es = get_dentry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); + if (!es) + return; + + if (es->num_entries < 3) + goto out; + + ep += 2; + + /* + * First entry : file entry + * Second entry : stream-extension entry + * Third entry : first file-name entry + * So, the index of first file-name dentry should start from 2. + */ + for (i = 2; i < es->num_entries; i++, ep++) { + /* end of name entry */ + if (exfat_get_entry_type(ep) != TYPE_EXTEND) + goto out; + + __extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i); + uniname += 15; + } + +out: + release_dentry_set(es); +} /* end of exfat_get_uniname_from_ext_entry */ + +static s32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + s32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 file entry + 1 stream entry + name entries */ + return((len-1) / 15 + 3); + +} /* end of exfat_calc_num_entries */ + +static s32 exfat_check_max_dentries(FILE_ID_T *fid) +{ + if ((fid->size >> DENTRY_SIZE_BITS) >= MAX_EXFAT_DENTRIES) { + /* exFAT spec allows a dir to grow upto 8388608(256MB) dentries */ + return -ENOSPC; + } + return 0; +} /* end of check_max_dentries */ + +/* + * Allocation Bitmap Management Functions + */ +s32 load_alloc_bmp(struct super_block *sb) +{ + s32 ret; + u32 i, j, map_size, type, need_map_size; + u64 sector; + CHAIN_T clu; + BMAP_DENTRY_T *ep; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + clu.dir = fsi->root_dir; + clu.flags = 0x01; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < fsi->dentries_per_clu; i++) { + ep = (BMAP_DENTRY_T *) get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = exfat_get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_BITMAP) + continue; + + if (ep->flags == 0x0) { + fsi->map_clu = le32_to_cpu(ep->start_clu); + map_size = (u32) le64_to_cpu(ep->size); + + need_map_size = (((fsi->num_clusters - CLUS_BASE) - 1) >> 3) + 1; + if (need_map_size != map_size) { + sdfat_log_msg(sb, KERN_ERR, + "bogus allocation bitmap size(need : %u, cur : %u)", + need_map_size, map_size); + /* Only allowed when bogus allocation bitmap size is large */ + if (need_map_size > map_size) + return -EIO; + } + fsi->map_sectors = ((need_map_size - 1) >> (sb->s_blocksize_bits)) + 1; + fsi->vol_amap = + kmalloc((sizeof(struct buffer_head *) * fsi->map_sectors), GFP_KERNEL); + if (!fsi->vol_amap) + return -ENOMEM; + + sector = CLUS_TO_SECT(fsi, fsi->map_clu); + + for (j = 0; j < fsi->map_sectors; j++) { + fsi->vol_amap[j] = NULL; + ret = read_sect(sb, sector+j, &(fsi->vol_amap[j]), 1); + if (ret) { + /* release all buffers and free vol_amap */ + i = 0; + while (i < j) + brelse(fsi->vol_amap[i++]); + + /* kfree(NULL) is safe */ + kfree(fsi->vol_amap); + fsi->vol_amap = NULL; + return ret; + } + } + + fsi->pbr_bh = NULL; + return 0; + } + } + + if (get_next_clus_safe(sb, &clu.dir)) + return -EIO; + } + + return -EINVAL; +} /* end of load_alloc_bmp */ + +void free_alloc_bmp(struct super_block *sb) +{ + s32 i; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + brelse(fsi->pbr_bh); + + for (i = 0; i < fsi->map_sectors; i++) + __brelse(fsi->vol_amap[i]); + + /* kfree(NULL) is safe */ + kfree(fsi->vol_amap); + fsi->vol_amap = NULL; +} + +/* WARN : + * If the value of "clu" is 0, it means cluster 2 which is + * the first cluster of cluster heap. + */ +static s32 set_alloc_bitmap(struct super_block *sb, u32 clu) +{ + s32 i, b; + u64 sector; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + i = clu >> (sb->s_blocksize_bits + 3); + b = clu & (u32)((sb->s_blocksize << 3) - 1); + + sector = CLUS_TO_SECT(fsi, fsi->map_clu) + i; + bitmap_set((unsigned long *)(fsi->vol_amap[i]->b_data), b, 1); + + return write_sect(sb, sector, fsi->vol_amap[i], 0); +} /* end of set_alloc_bitmap */ + +/* WARN : + * If the value of "clu" is 0, it means cluster 2 which is + * the first cluster of cluster heap. + */ +static s32 clr_alloc_bitmap(struct super_block *sb, u32 clu) +{ + s32 ret; + s32 i, b; + u64 sector; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct sdfat_mount_options *opts = &sbi->options; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + i = clu >> (sb->s_blocksize_bits + 3); + b = clu & (u32)((sb->s_blocksize << 3) - 1); + + sector = CLUS_TO_SECT(fsi, fsi->map_clu) + i; + + bitmap_clear((unsigned long *)(fsi->vol_amap[i]->b_data), b, 1); + + ret = write_sect(sb, sector, fsi->vol_amap[i], 0); + + if (opts->discard) { + s32 ret_discard; + + TMSG("discard cluster(%08x)\n", clu+2); + ret_discard = sb_issue_discard(sb, CLUS_TO_SECT(fsi, clu+2), + (1 << fsi->sect_per_clus_bits), GFP_NOFS, 0); + + if (ret_discard == -EOPNOTSUPP) { + sdfat_msg(sb, KERN_ERR, + "discard not supported by device, disabling"); + opts->discard = 0; + } + } + + return ret; +} /* end of clr_alloc_bitmap */ + +/* WARN : + * If the value of "clu" is 0, it means cluster 2 which is + * the first cluster of cluster heap. + */ +static u32 test_alloc_bitmap(struct super_block *sb, u32 clu) +{ + u32 i, map_i, map_b; + u32 clu_base, clu_free; + u8 k, clu_mask; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + clu_base = (clu & ~(0x7)) + 2; + clu_mask = (1 << (clu - clu_base + 2)) - 1; + + map_i = clu >> (sb->s_blocksize_bits + 3); + map_b = (clu >> 3) & (u32)(sb->s_blocksize - 1); + + for (i = 2; i < fsi->num_clusters; i += 8) { + k = *(((u8 *) fsi->vol_amap[map_i]->b_data) + map_b); + if (clu_mask > 0) { + k |= clu_mask; + clu_mask = 0; + } + if (k < 0xFF) { + clu_free = clu_base + free_bit[k]; + if (clu_free < fsi->num_clusters) + return clu_free; + } + clu_base += 8; + + if (((++map_b) >= (u32)sb->s_blocksize) || + (clu_base >= fsi->num_clusters)) { + if ((++map_i) >= fsi->map_sectors) { + clu_base = 2; + map_i = 0; + } + map_b = 0; + } + } + + return CLUS_EOF; +} /* end of test_alloc_bitmap */ + +void sync_alloc_bmp(struct super_block *sb) +{ + s32 i; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (fsi->vol_amap == NULL) + return; + + for (i = 0; i < fsi->map_sectors; i++) + sync_dirty_buffer(fsi->vol_amap[i]); +} + +static s32 exfat_chain_cont_cluster(struct super_block *sb, u32 chain, u32 len) +{ + if (!len) + return 0; + + while (len > 1) { + if (fat_ent_set(sb, chain, chain+1)) + return -EIO; + chain++; + len--; + } + + if (fat_ent_set(sb, chain, CLUS_EOF)) + return -EIO; + return 0; +} + +s32 chain_cont_cluster(struct super_block *sb, u32 chain, u32 len) +{ + return exfat_chain_cont_cluster(sb, chain, len); +} + + +static s32 exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + s32 ret = -EIO; + u32 num_clusters = 0; + u32 clu; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 i; + u64 sector; + + /* invalid cluster number */ + if (IS_CLUS_FREE(p_chain->dir) || IS_CLUS_EOF(p_chain->dir)) + return 0; + + /* no cluster to truncate */ + if (p_chain->size == 0) { + DMSG("%s: cluster(%u) truncation is not required.", + __func__, p_chain->dir); + return 0; + } + + /* check cluster validation */ + if ((p_chain->dir < 2) && (p_chain->dir >= fsi->num_clusters)) { + EMSG("%s: invalid start cluster (%u)\n", __func__, p_chain->dir); + sdfat_debug_bug_on(1); + return -EIO; + } + + set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + do { + if (do_relse) { + sector = CLUS_TO_SECT(fsi, clu); + for (i = 0; i < fsi->sect_per_clus; i++) { + if (dcache_release(sb, sector+i) == -EIO) + goto out; + } + } + + if (clr_alloc_bitmap(sb, clu-2)) + goto out; + clu++; + + num_clusters++; + } while (num_clusters < p_chain->size); + } else { + do { + if (do_relse) { + sector = CLUS_TO_SECT(fsi, clu); + for (i = 0; i < fsi->sect_per_clus; i++) { + if (dcache_release(sb, sector+i) == -EIO) + goto out; + } + } + + if (clr_alloc_bitmap(sb, (clu - CLUS_BASE))) + goto out; + + if (get_next_clus_safe(sb, &clu)) + goto out; + + num_clusters++; + } while (!IS_CLUS_EOF(clu)); + } + + /* success */ + ret = 0; +out: + + fsi->used_clusters -= num_clusters; + return ret; +} /* end of exfat_free_cluster */ + +static s32 exfat_alloc_cluster(struct super_block *sb, u32 num_alloc, CHAIN_T *p_chain, s32 dest) +{ + s32 ret = -ENOSPC; + u32 num_clusters = 0, total_cnt; + u32 hint_clu, new_clu, last_clu = CLUS_EOF; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + total_cnt = fsi->num_clusters - CLUS_BASE; + + if (unlikely(total_cnt < fsi->used_clusters)) { + sdfat_fs_error_ratelimit(sb, + "%s: invalid used clusters(t:%u,u:%u)\n", + __func__, total_cnt, fsi->used_clusters); + return -EIO; + } + + if (num_alloc > total_cnt - fsi->used_clusters) + return -ENOSPC; + + hint_clu = p_chain->dir; + /* find new cluster */ + if (IS_CLUS_EOF(hint_clu)) { + if (fsi->clu_srch_ptr < CLUS_BASE) { + EMSG("%s: fsi->clu_srch_ptr is invalid (%u)\n", + __func__, fsi->clu_srch_ptr); + ASSERT(0); + fsi->clu_srch_ptr = CLUS_BASE; + } + + hint_clu = test_alloc_bitmap(sb, fsi->clu_srch_ptr - CLUS_BASE); + if (IS_CLUS_EOF(hint_clu)) + return -ENOSPC; + } + + /* check cluster validation */ + if ((hint_clu < CLUS_BASE) && (hint_clu >= fsi->num_clusters)) { + EMSG("%s: hint_cluster is invalid (%u)\n", __func__, hint_clu); + ASSERT(0); + hint_clu = CLUS_BASE; + if (p_chain->flags == 0x03) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters)) + return -EIO; + p_chain->flags = 0x01; + } + } + + set_sb_dirty(sb); + + p_chain->dir = CLUS_EOF; + + while ((new_clu = test_alloc_bitmap(sb, hint_clu - CLUS_BASE)) != CLUS_EOF) { + if ((new_clu != hint_clu) && (p_chain->flags == 0x03)) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters)) { + ret = -EIO; + goto error; + } + p_chain->flags = 0x01; + } + + /* update allocation bitmap */ + if (set_alloc_bitmap(sb, new_clu - CLUS_BASE)) { + ret = -EIO; + goto error; + } + + num_clusters++; + + /* update FAT table */ + if (p_chain->flags == 0x01) { + if (fat_ent_set(sb, new_clu, CLUS_EOF)) { + ret = -EIO; + goto error; + } + } + + if (IS_CLUS_EOF(p_chain->dir)) { + p_chain->dir = new_clu; + } else if (p_chain->flags == 0x01) { + if (fat_ent_set(sb, last_clu, new_clu)) { + ret = -EIO; + goto error; + } + } + last_clu = new_clu; + + if ((--num_alloc) == 0) { + fsi->clu_srch_ptr = hint_clu; + fsi->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return 0; + } + + hint_clu = new_clu + 1; + if (hint_clu >= fsi->num_clusters) { + hint_clu = CLUS_BASE; + + if (p_chain->flags == 0x03) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters)) { + ret = -EIO; + goto error; + } + p_chain->flags = 0x01; + } + } + } +error: + if (num_clusters) + exfat_free_cluster(sb, p_chain, 0); + return ret; +} /* end of exfat_alloc_cluster */ + +static s32 exfat_count_used_clusters(struct super_block *sb, u32 *ret_count) +{ + u32 count = 0; + u32 i, map_i, map_b; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u32 total_clus = fsi->num_clusters - 2; + + map_i = map_b = 0; + + for (i = 0; i < total_clus; i += 8) { + u8 k = *(((u8 *) fsi->vol_amap[map_i]->b_data) + map_b); + + count += used_bit[k]; + if ((++map_b) >= (u32)sb->s_blocksize) { + map_i++; + map_b = 0; + } + } + + /* FIXME : abnormal bitmap count should be handled as more smart */ + if (total_clus < count) + count = total_clus; + + *ret_count = count; + return 0; +} /* end of exfat_count_used_clusters */ + + +/* + * File Operation Functions + */ +static FS_FUNC_T exfat_fs_func = { + .alloc_cluster = exfat_alloc_cluster, + .free_cluster = exfat_free_cluster, + .count_used_clusters = exfat_count_used_clusters, + + .init_dir_entry = exfat_init_dir_entry, + .init_ext_entry = exfat_init_ext_entry, + .find_dir_entry = exfat_find_dir_entry, + .delete_dir_entry = exfat_delete_dir_entry, + .get_uniname_from_ext_entry = exfat_get_uniname_from_ext_entry, + .count_ext_entries = exfat_count_ext_entries, + .calc_num_entries = exfat_calc_num_entries, + .check_max_dentries = exfat_check_max_dentries, + + .get_entry_type = exfat_get_entry_type, + .set_entry_type = exfat_set_entry_type, + .get_entry_attr = exfat_get_entry_attr, + .set_entry_attr = exfat_set_entry_attr, + .get_entry_flag = exfat_get_entry_flag, + .set_entry_flag = exfat_set_entry_flag, + .get_entry_clu0 = exfat_get_entry_clu0, + .set_entry_clu0 = exfat_set_entry_clu0, + .get_entry_size = exfat_get_entry_size, + .set_entry_size = exfat_set_entry_size, + .get_entry_time = exfat_get_entry_time, + .set_entry_time = exfat_set_entry_time, +}; + +s32 mount_exfat(struct super_block *sb, pbr_t *p_pbr) +{ + pbr64_t *p_bpb = (pbr64_t *)p_pbr; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (!p_bpb->bsx.num_fats) { + sdfat_msg(sb, KERN_ERR, "bogus number of FAT structure"); + return -EINVAL; + } + + fsi->sect_per_clus = 1 << p_bpb->bsx.sect_per_clus_bits; + fsi->sect_per_clus_bits = p_bpb->bsx.sect_per_clus_bits; + fsi->cluster_size_bits = fsi->sect_per_clus_bits + sb->s_blocksize_bits; + fsi->cluster_size = 1 << fsi->cluster_size_bits; + + fsi->num_FAT_sectors = le32_to_cpu(p_bpb->bsx.fat_length); + + fsi->FAT1_start_sector = le32_to_cpu(p_bpb->bsx.fat_offset); + if (p_bpb->bsx.num_fats == 1) + fsi->FAT2_start_sector = fsi->FAT1_start_sector; + else + fsi->FAT2_start_sector = fsi->FAT1_start_sector + fsi->num_FAT_sectors; + + fsi->root_start_sector = le32_to_cpu(p_bpb->bsx.clu_offset); + fsi->data_start_sector = fsi->root_start_sector; + + fsi->num_sectors = le64_to_cpu(p_bpb->bsx.vol_length); + fsi->num_clusters = le32_to_cpu(p_bpb->bsx.clu_count) + 2; + /* because the cluster index starts with 2 */ + + fsi->vol_type = EXFAT; + fsi->vol_id = le32_to_cpu(p_bpb->bsx.vol_serial); + + fsi->root_dir = le32_to_cpu(p_bpb->bsx.root_cluster); + fsi->dentries_in_root = 0; + fsi->dentries_per_clu = 1 << (fsi->cluster_size_bits - DENTRY_SIZE_BITS); + + fsi->vol_flag = (u32) le16_to_cpu(p_bpb->bsx.vol_flags); + fsi->clu_srch_ptr = CLUS_BASE; + fsi->used_clusters = (u32) ~0; + + fsi->fs_func = &exfat_fs_func; + fat_ent_ops_init(sb); + + if (p_bpb->bsx.vol_flags & VOL_DIRTY) { + fsi->vol_flag |= VOL_DIRTY; + sdfat_log_msg(sb, KERN_WARNING, "Volume was not properly " + "unmounted. Some data may be corrupt. " + "Please run fsck."); + } + + return 0; +} /* end of mount_exfat */ + +/* end of core_exfat.c */ diff --git a/fs/sdfat/core_fat.c b/fs/sdfat/core_fat.c new file mode 100644 index 0000000000000..493e5cb7e71a4 --- /dev/null +++ b/fs/sdfat/core_fat.c @@ -0,0 +1,1463 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : core_fat.c */ +/* PURPOSE : FAT-fs core code for sdFAT */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include +#include + +#include "sdfat.h" +#include "core.h" +#include +#include + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ +#define MAX_LFN_ORDER (20) + +/* + * MAX_EST_AU_SECT should be changed according to 32/64bits. + * On 32bit, 4KB page supports 512 clusters per AU. + * But, on 64bit, 4KB page can handle a half of total list_head of 32bit's. + * Bcause the size of list_head structure on 64bit increases twofold over 32bit. + */ +#if (BITS_PER_LONG == 64) +//#define MAX_EST_AU_SECT (16384) /* upto 8MB */ +#define MAX_EST_AU_SECT (32768) /* upto 16MB, used more page for list_head */ +#else +#define MAX_EST_AU_SECT (32768) /* upto 16MB */ +#endif + +/*======================================================================*/ +/* Local Function Declarations */ +/*======================================================================*/ +static s32 __extract_uni_name_from_ext_entry(EXT_DENTRY_T *, u16 *, s32); + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ +static u32 __calc_default_au_size(struct super_block *sb) +{ + struct block_device *bdev = sb->s_bdev; + struct gendisk *disk; + struct request_queue *queue; + struct queue_limits *limit; + unsigned int est_au_sect = MAX_EST_AU_SECT; + unsigned int est_au_size = 0; + unsigned int queue_au_size = 0; + sector_t total_sect = 0; + + /* we assumed that sector size is 512 bytes */ + + disk = bdev->bd_disk; + if (!disk) + goto out; + + queue = disk->queue; + if (!queue) + goto out; + + limit = &queue->limits; + queue_au_size = limit->discard_granularity; + + /* estimate function(x) = + * (total_sect / 2) * 512 / 1024 + * => (total_sect >> 1) >> 1) + * => (total_sect >> 2) + * => estimated bytes size + * + * ex1) <= 8GB -> 4MB + * ex2) 16GB -> 8MB + * ex3) >= 32GB -> 16MB + */ + total_sect = disk->part0.nr_sects; + est_au_size = total_sect >> 2; + + /* au_size assumed that bytes per sector is 512 */ + est_au_sect = est_au_size >> 9; + + MMSG("DBG1: total_sect(%llu) est_au_size(%u) est_au_sect(%u)\n", + (u64)total_sect, est_au_size, est_au_sect); + + if (est_au_sect <= 8192) { + /* 4MB */ + est_au_sect = 8192; + } else if (est_au_sect <= 16384) { + /* 8MB */ + est_au_sect = 16384; + } else { + /* 8MB or 16MB */ + est_au_sect = MAX_EST_AU_SECT; + } + + MMSG("DBG2: total_sect(%llu) est_au_size(%u) est_au_sect(%u)\n", + (u64)total_sect, est_au_size, est_au_sect); + + if (est_au_size < queue_au_size && + queue_au_size <= (MAX_EST_AU_SECT << 9)) { + DMSG("use queue_au_size(%u) instead of est_au_size(%u)\n", + queue_au_size, est_au_size); + est_au_sect = queue_au_size >> 9; + } + +out: + if (sb->s_blocksize != 512) { + ASSERT(sb->s_blocksize_bits > 9); + sdfat_log_msg(sb, KERN_INFO, + "adjustment est_au_size by logical block size(%lu)", + sb->s_blocksize); + est_au_sect >>= (sb->s_blocksize_bits - 9); + } + + sdfat_log_msg(sb, KERN_INFO, "set default AU sectors : %u " + "(queue_au_size : %u KB, disk_size : %llu MB)", + est_au_sect, queue_au_size >> 10, (u64)(total_sect >> 11)); + return est_au_sect; +} + + +/* + * Cluster Management Functions + */ +static s32 fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + s32 ret = -EIO; + s32 num_clusters = 0; + u32 clu, prev; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 i; + u64 sector; + + /* invalid cluster number */ + if (IS_CLUS_FREE(p_chain->dir) || IS_CLUS_EOF(p_chain->dir)) + return 0; + + /* no cluster to truncate */ + if (!p_chain->size) { + DMSG("%s: cluster(%u) truncation is not required.", + __func__, p_chain->dir); + return 0; + } + + /* check cluster validation */ + if ((p_chain->dir < 2) && (p_chain->dir >= fsi->num_clusters)) { + EMSG("%s: invalid start cluster (%u)\n", __func__, p_chain->dir); + sdfat_debug_bug_on(1); + return -EIO; + } + + + set_sb_dirty(sb); + clu = p_chain->dir; + + do { + if (do_relse) { + sector = CLUS_TO_SECT(fsi, clu); + for (i = 0; i < fsi->sect_per_clus; i++) { + if (dcache_release(sb, sector+i) == -EIO) + goto out; + } + } + + prev = clu; + if (get_next_clus_safe(sb, &clu)) { + /* print more helpful log */ + if (IS_CLUS_BAD(clu)) { + sdfat_log_msg(sb, KERN_ERR, "%s : " + "deleting bad cluster (clu[%u]->BAD)", + __func__, prev); + } else if (IS_CLUS_FREE(clu)) { + sdfat_log_msg(sb, KERN_ERR, "%s : " + "deleting free cluster (clu[%u]->FREE)", + __func__, prev); + } + goto out; + } + + /* Free FAT chain */ + if (fat_ent_set(sb, prev, CLUS_FREE)) + goto out; + + /* Update AMAP if needed */ + if (fsi->amap) { + if (amap_release_cluster(sb, prev)) + return -EIO; + } + + num_clusters++; + + } while (!IS_CLUS_EOF(clu)); + + /* success */ + ret = 0; +out: + fsi->used_clusters -= num_clusters; + return ret; +} /* end of fat_free_cluster */ + +static s32 fat_alloc_cluster(struct super_block *sb, u32 num_alloc, CHAIN_T *p_chain, s32 dest) +{ + s32 ret = -ENOSPC; + u32 i, num_clusters = 0, total_cnt; + u32 new_clu, last_clu = CLUS_EOF, read_clu; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + total_cnt = fsi->num_clusters - CLUS_BASE; + + if (unlikely(total_cnt < fsi->used_clusters)) { + sdfat_fs_error_ratelimit(sb, + "%s : invalid used clusters(t:%u,u:%u)\n", + __func__, total_cnt, fsi->used_clusters); + return -EIO; + } + + if (num_alloc > total_cnt - fsi->used_clusters) + return -ENOSPC; + + new_clu = p_chain->dir; + if (IS_CLUS_EOF(new_clu)) + new_clu = fsi->clu_srch_ptr; + else if (new_clu >= fsi->num_clusters) + new_clu = CLUS_BASE; + + set_sb_dirty(sb); + + p_chain->dir = CLUS_EOF; + + for (i = CLUS_BASE; i < fsi->num_clusters; i++) { + if (fat_ent_get(sb, new_clu, &read_clu)) { + ret = -EIO; + goto error; + } + + if (IS_CLUS_FREE(read_clu)) { + if (fat_ent_set(sb, new_clu, CLUS_EOF)) { + ret = -EIO; + goto error; + } + num_clusters++; + + if (IS_CLUS_EOF(p_chain->dir)) { + p_chain->dir = new_clu; + } else { + if (fat_ent_set(sb, last_clu, new_clu)) { + ret = -EIO; + goto error; + } + } + + last_clu = new_clu; + + if ((--num_alloc) == 0) { + fsi->clu_srch_ptr = new_clu; + fsi->used_clusters += num_clusters; + + return 0; + } + } + if ((++new_clu) >= fsi->num_clusters) + new_clu = CLUS_BASE; + } +error: + if (num_clusters) + fat_free_cluster(sb, p_chain, 0); + return ret; +} /* end of fat_alloc_cluster */ + +static s32 fat_count_used_clusters(struct super_block *sb, u32 *ret_count) +{ + s32 i; + u32 clu, count = 0; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + for (i = CLUS_BASE; i < fsi->num_clusters; i++) { + if (fat_ent_get(sb, i, &clu)) + return -EIO; + + if (!IS_CLUS_FREE(clu)) + count++; + } + + *ret_count = count; + return 0; +} /* end of fat_count_used_clusters */ + + +/* + * Directory Entry Management Functions + */ +static u32 fat_get_entry_type(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + + /* first byte of 32bytes dummy */ + if (*(ep->name) == MSDOS_UNUSED) + return TYPE_UNUSED; + + /* 0xE5 of Kanji Japanese is replaced to 0x05 */ + else if (*(ep->name) == MSDOS_DELETED) + return TYPE_DELETED; + + /* 11th byte of 32bytes dummy */ + else if ((ep->attr & ATTR_EXTEND_MASK) == ATTR_EXTEND) + return TYPE_EXTEND; + + else if (!(ep->attr & (ATTR_SUBDIR | ATTR_VOLUME))) + return TYPE_FILE; + + else if ((ep->attr & (ATTR_SUBDIR | ATTR_VOLUME)) == ATTR_SUBDIR) + return TYPE_DIR; + + else if ((ep->attr & (ATTR_SUBDIR | ATTR_VOLUME)) == ATTR_VOLUME) + return TYPE_VOLUME; + + return TYPE_INVALID; +} /* end of fat_get_entry_type */ + +static void fat_set_entry_type(DENTRY_T *p_entry, u32 type) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + + if (type == TYPE_UNUSED) + *(ep->name) = MSDOS_UNUSED; /* 0x0 */ + + else if (type == TYPE_DELETED) + *(ep->name) = MSDOS_DELETED; /* 0xE5 */ + + else if (type == TYPE_EXTEND) + ep->attr = ATTR_EXTEND; + + else if (type == TYPE_DIR) + ep->attr = ATTR_SUBDIR; + + else if (type == TYPE_FILE) + ep->attr = ATTR_ARCHIVE; + + else if (type == TYPE_SYMLINK) + ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK; +} /* end of fat_set_entry_type */ + +static u32 fat_get_entry_attr(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + + return (u32)ep->attr; +} /* end of fat_get_entry_attr */ + +static void fat_set_entry_attr(DENTRY_T *p_entry, u32 attr) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + + ep->attr = (u8)attr; +} /* end of fat_set_entry_attr */ + +static u8 fat_get_entry_flag(DENTRY_T *p_entry) +{ + return 0x01; +} /* end of fat_get_entry_flag */ + +static void fat_set_entry_flag(DENTRY_T *p_entry, u8 flags) +{ +} /* end of fat_set_entry_flag */ + +static u32 fat_get_entry_clu0(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + /* FIXME : is ok? */ + return(((u32)(le16_to_cpu(ep->start_clu_hi)) << 16) | le16_to_cpu(ep->start_clu_lo)); +} /* end of fat_get_entry_clu0 */ + +static void fat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + + ep->start_clu_lo = cpu_to_le16(CLUSTER_16(start_clu)); + ep->start_clu_hi = cpu_to_le16(CLUSTER_16(start_clu >> 16)); +} /* end of fat_set_entry_clu0 */ + +static u64 fat_get_entry_size(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + + return (u64)le32_to_cpu(ep->size); +} /* end of fat_get_entry_size */ + +static void fat_set_entry_size(DENTRY_T *p_entry, u64 size) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *)p_entry; + + ep->size = cpu_to_le32((u32)size); +} /* end of fat_set_entry_size */ + +static void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t = 0x00, d = 0x21; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + switch (mode) { + case TM_CREATE: + t = le16_to_cpu(ep->create_time); + d = le16_to_cpu(ep->create_date); + break; + case TM_MODIFY: + t = le16_to_cpu(ep->modify_time); + d = le16_to_cpu(ep->modify_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of fat_get_entry_time */ + +static void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t, d; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + ep->create_time = cpu_to_le16(t); + ep->create_date = cpu_to_le16(d); + break; + case TM_MODIFY: + ep->modify_time = cpu_to_le16(t); + ep->modify_date = cpu_to_le16(d); + break; + } +} /* end of fat_set_entry_time */ + +static void __init_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, u32 type, u32 start_clu) +{ + TIMESTAMP_T tm, *tp; + + fat_set_entry_type((DENTRY_T *) ep, type); + ep->start_clu_lo = cpu_to_le16(CLUSTER_16(start_clu)); + ep->start_clu_hi = cpu_to_le16(CLUSTER_16(start_clu >> 16)); + ep->size = 0; + + tp = tm_now(SDFAT_SB(sb), &tm); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + ep->access_date = 0; + ep->create_time_ms = 0; +} /* end of __init_dos_entry */ + +static void __init_ext_entry(EXT_DENTRY_T *ep, s32 order, u8 chksum, u16 *uniname) +{ + s32 i; + u8 end = false; + + fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->order = (u8) order; + ep->sysid = 0; + ep->checksum = chksum; + ep->start_clu = 0; + + /* unaligned name */ + for (i = 0; i < 5; i++) { + if (!end) { + put_unaligned_le16(*uniname, &(ep->unicode_0_4[i<<1])); + if (*uniname == 0x0) + end = true; + else + uniname++; + } else { + put_unaligned_le16(0xFFFF, &(ep->unicode_0_4[i<<1])); + } + } + + /* aligned name */ + for (i = 0; i < 6; i++) { + if (!end) { + ep->unicode_5_10[i] = cpu_to_le16(*uniname); + if (*uniname == 0x0) + end = true; + else + uniname++; + } else { + ep->unicode_5_10[i] = cpu_to_le16(0xFFFF); + } + } + + /* aligned name */ + for (i = 0; i < 2; i++) { + if (!end) { + ep->unicode_11_12[i] = cpu_to_le16(*uniname); + if (*uniname == 0x0) + end = true; + else + uniname++; + } else { + ep->unicode_11_12[i] = cpu_to_le16(0xFFFF); + } + } +} /* end of __init_ext_entry */ + +static s32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, + u32 start_clu, u64 size) +{ + u64 sector; + DOS_DENTRY_T *dos_ep; + + dos_ep = (DOS_DENTRY_T *) get_dentry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return -EIO; + + __init_dos_entry(sb, dos_ep, type, start_clu); + dcache_modify(sb, sector); + + return 0; +} /* end of fat_init_dir_entry */ + +static s32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + s32 i; + u64 sector; + u8 chksum; + u16 *uniname = p_uniname->name; + DOS_DENTRY_T *dos_ep; + EXT_DENTRY_T *ext_ep; + + dos_ep = (DOS_DENTRY_T *) get_dentry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return -EIO; + + dos_ep->lcase = p_dosname->name_case; + memcpy(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); + if (dcache_modify(sb, sector)) + return -EIO; + + if ((--num_entries) > 0) { + chksum = calc_chksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (i = 1; i < num_entries; i++) { + ext_ep = (EXT_DENTRY_T *) get_dentry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return -EIO; + + __init_ext_entry(ext_ep, i, chksum, uniname); + if (dcache_modify(sb, sector)) + return -EIO; + uniname += 13; + } + + ext_ep = (EXT_DENTRY_T *) get_dentry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return -EIO; + + __init_ext_entry(ext_ep, i+MSDOS_LAST_LFN, chksum, uniname); + if (dcache_modify(sb, sector)) + return -EIO; + } + + return 0; +} /* end of fat_init_ext_entry */ + +static s32 fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries) +{ + s32 i; + u64 sector; + DENTRY_T *ep; + + for (i = num_entries-1; i >= order; i--) { + ep = get_dentry_in_dir(sb, p_dir, entry-i, §or); + if (!ep) + return -EIO; + + fat_set_entry_type(ep, TYPE_DELETED); + if (dcache_modify(sb, sector)) + return -EIO; + } + + return 0; +} + +/* return values of fat_find_dir_entry() + * >= 0 : return dir entiry position with the name in dir + * -EEXIST : (root dir, ".") it is the root dir itself + * -ENOENT : entry with the name does not exist + * -EIO : I/O error + */ +static inline s32 __get_dentries_per_clu(FS_INFO_T *fsi, s32 clu) +{ + if (IS_CLUS_FREE(clu)) /* FAT16 root_dir */ + return fsi->dentries_in_root; + + return fsi->dentries_per_clu; +} + +static s32 fat_find_dir_entry(struct super_block *sb, FILE_ID_T *fid, + CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type) +{ + s32 i, rewind = 0, dentry = 0, end_eidx = 0; + s32 chksum = 0, lfn_ord = 0, lfn_len = 0; + s32 dentries_per_clu, num_empty = 0; + u32 entry_type; + u16 entry_uniname[14], *uniname = NULL; + CHAIN_T clu; + DENTRY_T *ep; + HINT_T *hint_stat = &fid->hint_stat; + HINT_FEMP_T candi_empty; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* + * REMARK: + * DOT and DOTDOT are handled by VFS layer + */ + + dentries_per_clu = __get_dentries_per_clu(fsi, p_dir->dir); + clu.dir = p_dir->dir; + clu.flags = p_dir->flags; + + if (hint_stat->eidx) { + clu.dir = hint_stat->clu; + dentry = hint_stat->eidx; + end_eidx = dentry; + } + + candi_empty.eidx = -1; + + MMSG("lookup dir= %s\n", p_dosname->name); +rewind: + while (!IS_CLUS_EOF(clu.dir)) { + i = dentry % dentries_per_clu; + for (; i < dentries_per_clu; i++, dentry++) { + if (rewind && (dentry == end_eidx)) + goto not_found; + + ep = get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + entry_type = fat_get_entry_type(ep); + + /* + * Most directory entries have long name, + * So, we check extend directory entry first. + */ + if (entry_type == TYPE_EXTEND) { + EXT_DENTRY_T *ext_ep = (EXT_DENTRY_T *)ep; + u32 cur_ord = (u32)ext_ep->order; + u32 cur_chksum = (s32)ext_ep->checksum; + s32 len = 13; + u16 unichar; + + num_empty = 0; + candi_empty.eidx = -1; + + /* check whether new lfn or not */ + if (cur_ord & MSDOS_LAST_LFN) { + cur_ord &= ~(MSDOS_LAST_LFN); + chksum = cur_chksum; + len = (13 * (cur_ord-1)); + uniname = (p_uniname->name + len); + lfn_ord = cur_ord + 1; + lfn_len = 0; + + /* check minimum name length */ + if (cur_ord && + (len > p_uniname->name_len)) { + /* MISMATCHED NAME LENGTH */ + lfn_len = -1; + } + len = 0; + } + + /* invalid lfn order */ + if (!cur_ord || (cur_ord > MAX_LFN_ORDER) || + ((cur_ord + 1) != lfn_ord)) + goto reset_dentry_set; + + /* check checksum of directory entry set */ + if (cur_chksum != chksum) + goto reset_dentry_set; + + /* update order for next dentry */ + lfn_ord = cur_ord; + + /* check whether mismatched lfn or not */ + if (lfn_len == -1) { + /* MISMATCHED LFN DENTRY SET */ + continue; + } + + if (!uniname) { + sdfat_fs_error(sb, + "%s : abnormal dentry " + "(start_clu[%u], " + "idx[%u])", __func__, + p_dir->dir, dentry); + sdfat_debug_bug_on(1); + return -EIO; + } + + /* update position of name buffer */ + uniname -= len; + + /* get utf16 characters saved on this entry */ + len = __extract_uni_name_from_ext_entry(ext_ep, entry_uniname, lfn_ord); + + /* replace last char to null */ + unichar = *(uniname+len); + *(uniname+len) = (u16)0x0; + + /* uniname ext_dentry unit compare repeatdly */ + if (nls_cmp_uniname(sb, uniname, entry_uniname)) { + /* DO HANDLE WRONG NAME */ + lfn_len = -1; + } else { + /* add matched chars length */ + lfn_len += len; + } + + /* restore previous character */ + *(uniname+len) = unichar; + + /* jump to check next dentry */ + continue; + + } else if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *)ep; + u32 cur_chksum = (s32)calc_chksum_1byte( + (void *) dos_ep->name, + DOS_NAME_LENGTH, 0); + + num_empty = 0; + candi_empty.eidx = -1; + + MMSG("checking dir= %c%c%c%c%c%c%c%c%c%c%c\n", + dos_ep->name[0], dos_ep->name[1], + dos_ep->name[2], dos_ep->name[3], + dos_ep->name[4], dos_ep->name[5], + dos_ep->name[6], dos_ep->name[7], + dos_ep->name[8], dos_ep->name[9], + dos_ep->name[10]); + + /* + * if there is no valid long filename, + * we should check short filename. + */ + if (!lfn_len || (cur_chksum != chksum)) { + /* check shortname */ + if ((p_dosname->name[0] != '\0') && + !nls_cmp_sfn(sb, + p_dosname->name, + dos_ep->name)) { + goto found; + } + /* check name length */ + } else if ((lfn_len > 0) && + ((s32)p_uniname->name_len == + lfn_len)) { + goto found; + } + + /* DO HANDLE MISMATCHED SFN, FALL THROUGH */ + } else if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { + num_empty++; + if (candi_empty.eidx == -1) { + if (num_empty == 1) { + candi_empty.cur.dir = clu.dir; + candi_empty.cur.size = clu.size; + candi_empty.cur.flags = clu.flags; + } + + if (num_empty >= num_entries) { + candi_empty.eidx = dentry - (num_empty - 1); + ASSERT(0 <= candi_empty.eidx); + candi_empty.count = num_empty; + + if ((fid->hint_femp.eidx == -1) || + (candi_empty.eidx <= fid->hint_femp.eidx)) { + memcpy(&fid->hint_femp, + &candi_empty, + sizeof(HINT_FEMP_T)); + } + } + } + + if (entry_type == TYPE_UNUSED) + goto not_found; + /* FALL THROUGH */ + } +reset_dentry_set: + /* TYPE_DELETED, TYPE_VOLUME OR MISMATCHED SFN */ + lfn_ord = 0; + lfn_len = 0; + chksum = 0; + } + + if (IS_CLUS_FREE(p_dir->dir)) + break; /* FAT16 root_dir */ + + if (get_next_clus_safe(sb, &clu.dir)) + return -EIO; + } + +not_found: + /* we started at not 0 index,so we should try to find target + * from 0 index to the index we started at. + */ + if (!rewind && end_eidx) { + rewind = 1; + dentry = 0; + clu.dir = p_dir->dir; + /* reset dentry set */ + lfn_ord = 0; + lfn_len = 0; + chksum = 0; + /* reset empty hint_*/ + num_empty = 0; + candi_empty.eidx = -1; + goto rewind; + } + + /* initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + return -ENOENT; + +found: + /* next dentry we'll find is out of this cluster */ + if (!((dentry + 1) % dentries_per_clu)) { + int ret = 0; + /* FAT16 root_dir */ + if (IS_CLUS_FREE(p_dir->dir)) + clu.dir = CLUS_EOF; + else + ret = get_next_clus_safe(sb, &clu.dir); + + if (ret || IS_CLUS_EOF(clu.dir)) { + /* just initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + return dentry; + } + } + + hint_stat->clu = clu.dir; + hint_stat->eidx = dentry + 1; + return dentry; +} /* end of fat_find_dir_entry */ + +/* returns -EIO on error */ +static s32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry) +{ + s32 count = 0; + u8 chksum; + DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry; + EXT_DENTRY_T *ext_ep; + + chksum = calc_chksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (entry--; entry >= 0; entry--) { + ext_ep = (EXT_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -EIO; + + if ((fat_get_entry_type((DENTRY_T *)ext_ep) == TYPE_EXTEND) && + (ext_ep->checksum == chksum)) { + count++; + if (ext_ep->order > MSDOS_LAST_LFN) + return count; + } else { + return count; + } + } + + return count; +} + + +/* + * Name Conversion Functions + */ +static s32 __extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, u16 *uniname, s32 order) +{ + s32 i, len = 0; + + for (i = 0; i < 5; i++) { + *uniname = get_unaligned_le16(&(ep->unicode_0_4[i<<1])); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + if (order < 20) { + for (i = 0; i < 6; i++) { + /* FIXME : unaligned? */ + *uniname = le16_to_cpu(ep->unicode_5_10[i]); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + } else { + for (i = 0; i < 4; i++) { + /* FIXME : unaligned? */ + *uniname = le16_to_cpu(ep->unicode_5_10[i]); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + *uniname = 0x0; /* uniname[MAX_NAME_LENGTH] */ + return len; + } + + for (i = 0; i < 2; i++) { + /* FIXME : unaligned? */ + *uniname = le16_to_cpu(ep->unicode_11_12[i]); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} /* end of __extract_uni_name_from_ext_entry */ + +static void fat_get_uniname_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname) +{ + u32 i; + u16 *name = uniname; + u32 chksum; + + DOS_DENTRY_T *dos_ep = + (DOS_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry, NULL); + + if (unlikely(!dos_ep)) + goto invalid_lfn; + + chksum = (u32)calc_chksum_1byte( + (void *) dos_ep->name, + DOS_NAME_LENGTH, 0); + + for (entry--, i = 1; entry >= 0; entry--, i++) { + EXT_DENTRY_T *ep; + + ep = (EXT_DENTRY_T *)get_dentry_in_dir(sb, p_dir, entry, NULL); + if (!ep) + goto invalid_lfn; + + if (fat_get_entry_type((DENTRY_T *) ep) != TYPE_EXTEND) + goto invalid_lfn; + + if (chksum != (u32)ep->checksum) + goto invalid_lfn; + + if (i != (u32)(ep->order & ~(MSDOS_LAST_LFN))) + goto invalid_lfn; + + __extract_uni_name_from_ext_entry(ep, name, (s32)i); + if (ep->order & MSDOS_LAST_LFN) + return; + + name += 13; + } +invalid_lfn: + *uniname = (u16)0x0; +} /* end of fat_get_uniname_from_ext_entry */ + +/* Find if the shortname exists + * and check if there are free entries + */ +static s32 __fat_find_shortname_entry(struct super_block *sb, CHAIN_T *p_dir, + u8 *p_dosname, s32 *offset, __attribute__((unused))int n_entry_needed) +{ + u32 type; + s32 i, dentry = 0; + s32 dentries_per_clu; + DENTRY_T *ep = NULL; + DOS_DENTRY_T *dos_ep = NULL; + CHAIN_T clu = *p_dir; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (offset) + *offset = -1; + + if (IS_CLUS_FREE(clu.dir)) /* FAT16 root_dir */ + dentries_per_clu = fsi->dentries_in_root; + else + dentries_per_clu = fsi->dentries_per_clu; + + while (!IS_CLUS_EOF(clu.dir)) { + for (i = 0; i < dentries_per_clu; i++, dentry++) { + ep = get_dentry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -EIO; + + type = fat_get_entry_type(ep); + + if ((type == TYPE_FILE) || (type == TYPE_DIR)) { + dos_ep = (DOS_DENTRY_T *)ep; + if (!nls_cmp_sfn(sb, p_dosname, dos_ep->name)) { + if (offset) + *offset = dentry; + return 0; + } + } + } + + /* fat12/16 root dir */ + if (IS_CLUS_FREE(clu.dir)) + break; + + if (get_next_clus_safe(sb, &clu.dir)) + return -EIO; + } + return -ENOENT; +} + +#ifdef CONFIG_SDFAT_FAT32_SHORTNAME_SEQ +static void __fat_attach_count_to_dos_name(u8 *dosname, s32 count) +{ + s32 i, j, length; + s8 str_count[6]; + + snprintf(str_count, sizeof(str_count), "~%d", count); + length = strlen(str_count); + + i = j = 0; + while (j <= (8 - length)) { + i = j; + if (dosname[j] == ' ') + break; + if (dosname[j] & 0x80) + j += 2; + else + j++; + } + + for (j = 0; j < length; i++, j++) + dosname[i] = (u8) str_count[j]; + + if (i == 7) + dosname[7] = ' '; + +} /* end of __fat_attach_count_to_dos_name */ +#endif + +s32 fat_generate_dos_name_new(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname, s32 n_entry_needed) +{ + s32 i; + s32 baselen, err; + u8 work[DOS_NAME_LENGTH], buf[5]; + u8 tail; + + baselen = 8; + memset(work, ' ', DOS_NAME_LENGTH); + memcpy(work, p_dosname->name, DOS_NAME_LENGTH); + + while (baselen && (work[--baselen] == ' ')) { + /* DO NOTHING, JUST FOR CHECK_PATCH */ + } + + if (baselen > 6) + baselen = 6; + + BUG_ON(baselen < 0); + +#ifdef CONFIG_SDFAT_FAT32_SHORTNAME_SEQ + /* example) namei_exfat.c -> NAMEI_~1 - NAMEI_~9 */ + work[baselen] = '~'; + for (i = 1; i < 10; i++) { + // '0' + i = 1 ~ 9 ASCII + work[baselen + 1] = '0' + i; + err = __fat_find_shortname_entry(sb, p_dir, work, NULL, n_entry_needed); + if (err == -ENOENT) { + /* void return */ + __fat_attach_count_to_dos_name(p_dosname->name, i); + return 0; + } + + /* any other error */ + if (err) + return err; + } +#endif + + i = jiffies; + tail = (jiffies >> 16) & 0x7; + + if (baselen > 2) + baselen = 2; + + BUG_ON(baselen < 0); + + work[baselen + 4] = '~'; + // 1 ~ 8 ASCII + work[baselen + 5] = '1' + tail; + while (1) { + snprintf(buf, sizeof(buf), "%04X", i & 0xffff); + memcpy(&work[baselen], buf, 4); + err = __fat_find_shortname_entry(sb, p_dir, work, NULL, n_entry_needed); + if (err == -ENOENT) { + memcpy(p_dosname->name, work, DOS_NAME_LENGTH); + break; + } + + /* any other error */ + if (err) + return err; + + i -= 11; + } + return 0; +} /* end of generate_dos_name_new */ + +static s32 fat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + s32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 dos name entry + extended entries */ + return((len-1) / 13 + 2); + +} /* end of calc_num_enties */ + +static s32 fat_check_max_dentries(FILE_ID_T *fid) +{ + if ((fid->size >> DENTRY_SIZE_BITS) >= MAX_FAT_DENTRIES) { + /* FAT spec allows a dir to grow upto 65536 dentries */ + return -ENOSPC; + } + return 0; +} /* end of check_max_dentries */ + + +/* + * File Operation Functions + */ +static FS_FUNC_T fat_fs_func = { + .alloc_cluster = fat_alloc_cluster, + .free_cluster = fat_free_cluster, + .count_used_clusters = fat_count_used_clusters, + + .init_dir_entry = fat_init_dir_entry, + .init_ext_entry = fat_init_ext_entry, + .find_dir_entry = fat_find_dir_entry, + .delete_dir_entry = fat_delete_dir_entry, + .get_uniname_from_ext_entry = fat_get_uniname_from_ext_entry, + .count_ext_entries = fat_count_ext_entries, + .calc_num_entries = fat_calc_num_entries, + .check_max_dentries = fat_check_max_dentries, + + .get_entry_type = fat_get_entry_type, + .set_entry_type = fat_set_entry_type, + .get_entry_attr = fat_get_entry_attr, + .set_entry_attr = fat_set_entry_attr, + .get_entry_flag = fat_get_entry_flag, + .set_entry_flag = fat_set_entry_flag, + .get_entry_clu0 = fat_get_entry_clu0, + .set_entry_clu0 = fat_set_entry_clu0, + .get_entry_size = fat_get_entry_size, + .set_entry_size = fat_set_entry_size, + .get_entry_time = fat_get_entry_time, + .set_entry_time = fat_set_entry_time, +}; + +static FS_FUNC_T amap_fat_fs_func = { + .alloc_cluster = amap_fat_alloc_cluster, + .free_cluster = fat_free_cluster, + .count_used_clusters = fat_count_used_clusters, + + .init_dir_entry = fat_init_dir_entry, + .init_ext_entry = fat_init_ext_entry, + .find_dir_entry = fat_find_dir_entry, + .delete_dir_entry = fat_delete_dir_entry, + .get_uniname_from_ext_entry = fat_get_uniname_from_ext_entry, + .count_ext_entries = fat_count_ext_entries, + .calc_num_entries = fat_calc_num_entries, + .check_max_dentries = fat_check_max_dentries, + + .get_entry_type = fat_get_entry_type, + .set_entry_type = fat_set_entry_type, + .get_entry_attr = fat_get_entry_attr, + .set_entry_attr = fat_set_entry_attr, + .get_entry_flag = fat_get_entry_flag, + .set_entry_flag = fat_set_entry_flag, + .get_entry_clu0 = fat_get_entry_clu0, + .set_entry_clu0 = fat_set_entry_clu0, + .get_entry_size = fat_get_entry_size, + .set_entry_size = fat_set_entry_size, + .get_entry_time = fat_get_entry_time, + .set_entry_time = fat_set_entry_time, + + .get_au_stat = amap_get_au_stat, +}; + +s32 mount_fat16(struct super_block *sb, pbr_t *p_pbr) +{ + s32 num_root_sectors; + bpb16_t *p_bpb = &(p_pbr->bpb.f16); + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (!p_bpb->num_fats) { + sdfat_msg(sb, KERN_ERR, "bogus number of FAT structure"); + return -EINVAL; + } + + num_root_sectors = get_unaligned_le16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS; + num_root_sectors = ((num_root_sectors-1) >> sb->s_blocksize_bits) + 1; + + fsi->sect_per_clus = p_bpb->sect_per_clus; + fsi->sect_per_clus_bits = ilog2(p_bpb->sect_per_clus); + fsi->cluster_size_bits = fsi->sect_per_clus_bits + sb->s_blocksize_bits; + fsi->cluster_size = 1 << fsi->cluster_size_bits; + + fsi->num_FAT_sectors = le16_to_cpu(p_bpb->num_fat_sectors); + + fsi->FAT1_start_sector = le16_to_cpu(p_bpb->num_reserved); + if (p_bpb->num_fats == 1) + fsi->FAT2_start_sector = fsi->FAT1_start_sector; + else + fsi->FAT2_start_sector = fsi->FAT1_start_sector + fsi->num_FAT_sectors; + + fsi->root_start_sector = fsi->FAT2_start_sector + fsi->num_FAT_sectors; + fsi->data_start_sector = fsi->root_start_sector + num_root_sectors; + + fsi->num_sectors = get_unaligned_le16(p_bpb->num_sectors); + if (!fsi->num_sectors) + fsi->num_sectors = le32_to_cpu(p_bpb->num_huge_sectors); + + if (!fsi->num_sectors) { + sdfat_msg(sb, KERN_ERR, "bogus number of total sector count"); + return -EINVAL; + } + + fsi->num_clusters = (u32)((fsi->num_sectors - fsi->data_start_sector) >> fsi->sect_per_clus_bits) + CLUS_BASE; + /* because the cluster index starts with 2 */ + + fsi->vol_type = FAT16; + if (fsi->num_clusters < FAT12_THRESHOLD) + fsi->vol_type = FAT12; + + fsi->vol_id = get_unaligned_le32(p_bpb->vol_serial); + + fsi->root_dir = 0; + fsi->dentries_in_root = get_unaligned_le16(p_bpb->num_root_entries); + if (!fsi->dentries_in_root) { + sdfat_msg(sb, KERN_ERR, "bogus number of max dentry count " + "of the root directory"); + return -EINVAL; + } + + fsi->dentries_per_clu = 1 << (fsi->cluster_size_bits - DENTRY_SIZE_BITS); + + fsi->vol_flag = VOL_CLEAN; + fsi->clu_srch_ptr = 2; + fsi->used_clusters = (u32) ~0; + + fsi->fs_func = &fat_fs_func; + fat_ent_ops_init(sb); + + if (p_bpb->state & FAT_VOL_DIRTY) { + fsi->vol_flag |= VOL_DIRTY; + sdfat_log_msg(sb, KERN_WARNING, "Volume was not properly " + "unmounted. Some data may be corrupt. " + "Please run fsck."); + } + + return 0; +} /* end of mount_fat16 */ + +static sector_t __calc_hidden_sect(struct super_block *sb) +{ + struct block_device *bdev = sb->s_bdev; + sector_t hidden = 0; + + if (!bdev) + goto out; + + hidden = bdev->bd_part->start_sect; + /* a disk device, not a partition */ + if (!hidden) { + ASSERT(bdev == bdev->bd_contains); + ASSERT(!bdev->bd_part->partno); + goto out; + } + + if (sb->s_blocksize_bits != 9) { + ASSERT(sb->s_blocksize_bits > 9); + hidden >>= (sb->s_blocksize_bits - 9); + } + +out: + sdfat_log_msg(sb, KERN_INFO, "start_sect of partition : %lld", + (s64)hidden); + return hidden; + +} + +s32 mount_fat32(struct super_block *sb, pbr_t *p_pbr) +{ + pbr32_t *p_bpb = (pbr32_t *)p_pbr; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (!p_bpb->bpb.num_fats) { + sdfat_msg(sb, KERN_ERR, "bogus number of FAT structure"); + return -EINVAL; + } + + fsi->sect_per_clus = p_bpb->bpb.sect_per_clus; + fsi->sect_per_clus_bits = ilog2(p_bpb->bpb.sect_per_clus); + fsi->cluster_size_bits = fsi->sect_per_clus_bits + sb->s_blocksize_bits; + fsi->cluster_size = 1 << fsi->cluster_size_bits; + + fsi->num_FAT_sectors = le32_to_cpu(p_bpb->bpb.num_fat32_sectors); + + fsi->FAT1_start_sector = le16_to_cpu(p_bpb->bpb.num_reserved); + if (p_bpb->bpb.num_fats == 1) + fsi->FAT2_start_sector = fsi->FAT1_start_sector; + else + fsi->FAT2_start_sector = fsi->FAT1_start_sector + fsi->num_FAT_sectors; + + fsi->root_start_sector = fsi->FAT2_start_sector + fsi->num_FAT_sectors; + fsi->data_start_sector = fsi->root_start_sector; + + /* SPEC violation for compatibility */ + fsi->num_sectors = get_unaligned_le16(p_bpb->bpb.num_sectors); + if (!fsi->num_sectors) + fsi->num_sectors = le32_to_cpu(p_bpb->bpb.num_huge_sectors); + + /* 2nd check */ + if (!fsi->num_sectors) { + sdfat_msg(sb, KERN_ERR, "bogus number of total sector count"); + return -EINVAL; + } + + fsi->num_clusters = (u32)((fsi->num_sectors - fsi->data_start_sector) >> fsi->sect_per_clus_bits) + CLUS_BASE; + /* because the cluster index starts with 2 */ + + fsi->vol_type = FAT32; + fsi->vol_id = get_unaligned_le32(p_bpb->bsx.vol_serial); + + fsi->root_dir = le32_to_cpu(p_bpb->bpb.root_cluster); + fsi->dentries_in_root = 0; + fsi->dentries_per_clu = 1 << (fsi->cluster_size_bits - DENTRY_SIZE_BITS); + + fsi->vol_flag = VOL_CLEAN; + fsi->clu_srch_ptr = 2; + fsi->used_clusters = (u32) ~0; + + fsi->fs_func = &fat_fs_func; + + /* Delayed / smart allocation related init */ + fsi->reserved_clusters = 0; + + /* Should be initialized before calling amap_create() */ + fat_ent_ops_init(sb); + + /* AU Map Creation */ + if (SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_SMART) { + u32 hidden_sectors = le32_to_cpu(p_bpb->bpb.num_hid_sectors); + u32 calc_hid_sect = 0; + int ret; + + + /* calculate hidden sector size */ + calc_hid_sect = __calc_hidden_sect(sb); + if (calc_hid_sect != hidden_sectors) { + sdfat_log_msg(sb, KERN_WARNING, "abnormal hidden " + "sector : bpb(%u) != ondisk(%u)", + hidden_sectors, calc_hid_sect); + if (SDFAT_SB(sb)->options.adj_hidsect) { + sdfat_log_msg(sb, KERN_INFO, + "adjustment hidden sector : " + "bpb(%u) -> ondisk(%u)", + hidden_sectors, calc_hid_sect); + hidden_sectors = calc_hid_sect; + } + } + + SDFAT_SB(sb)->options.amap_opt.misaligned_sect = hidden_sectors; + + /* calculate AU size if it's not set */ + if (!SDFAT_SB(sb)->options.amap_opt.sect_per_au) { + SDFAT_SB(sb)->options.amap_opt.sect_per_au = + __calc_default_au_size(sb); + } + + ret = amap_create(sb, + SDFAT_SB(sb)->options.amap_opt.pack_ratio, + SDFAT_SB(sb)->options.amap_opt.sect_per_au, + SDFAT_SB(sb)->options.amap_opt.misaligned_sect); + if (ret) { + sdfat_log_msg(sb, KERN_WARNING, "failed to create AMAP." + " disabling smart allocation. (err:%d)", ret); + SDFAT_SB(sb)->options.improved_allocation &= ~(SDFAT_ALLOC_SMART); + } else { + fsi->fs_func = &amap_fat_fs_func; + } + } + + /* Check dependency of mount options */ + if (SDFAT_SB(sb)->options.improved_allocation != + (SDFAT_ALLOC_DELAY | SDFAT_ALLOC_SMART)) { + sdfat_log_msg(sb, KERN_INFO, "disabling defragmentation because" + " smart, delay options are disabled"); + SDFAT_SB(sb)->options.defrag = 0; + } + + if (p_bpb->bsx.state & FAT_VOL_DIRTY) { + fsi->vol_flag |= VOL_DIRTY; + sdfat_log_msg(sb, KERN_WARNING, "Volume was not properly " + "unmounted. Some data may be corrupt. " + "Please run fsck."); + } + + return 0; +} /* end of mount_fat32 */ + +/* end of core_fat.c */ diff --git a/fs/sdfat/dfr.c b/fs/sdfat/dfr.c new file mode 100644 index 0000000000000..2853fe4251978 --- /dev/null +++ b/fs/sdfat/dfr.c @@ -0,0 +1,1372 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* @PROJECT : exFAT & FAT12/16/32 File System */ +/* @FILE : dfr.c */ +/* @PURPOSE : Defragmentation support for SDFAT32 */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include + +#include "sdfat.h" +#include "core.h" +#include "amap_smart.h" + +#ifdef CONFIG_SDFAT_DFR +/** + * @fn defrag_get_info + * @brief get HW params for defrag daemon + * @return 0 on success, -errno otherwise + * @param sb super block + * @param arg defrag info arguments + * @remark protected by super_block + */ +int +defrag_get_info( + IN struct super_block *sb, + OUT struct defrag_info_arg *arg) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + + if (!arg) + return -EINVAL; + + arg->sec_sz = sb->s_blocksize; + arg->clus_sz = fsi->cluster_size; + arg->total_sec = fsi->num_sectors; + arg->fat_offset_sec = fsi->FAT1_start_sector; + arg->fat_sz_sec = fsi->num_FAT_sectors; + arg->n_fat = (fsi->FAT1_start_sector == fsi->FAT2_start_sector) ? 1 : 2; + + arg->sec_per_au = amap->option.au_size; + arg->hidden_sectors = amap->option.au_align_factor % amap->option.au_size; + + return 0; +} + + +static int +__defrag_scan_dir( + IN struct super_block *sb, + IN DOS_DENTRY_T *dos_ep, + IN loff_t i_pos, + OUT struct defrag_trav_arg *arg) +{ + FS_INFO_T *fsi = NULL; + UNI_NAME_T uniname; + unsigned int type = 0, start_clus = 0; + int err = -EPERM; + + /* Check params */ + ERR_HANDLE2((!sb || !dos_ep || !i_pos || !arg), err, -EINVAL); + fsi = &(SDFAT_SB(sb)->fsi); + + /* Get given entry's type */ + type = fsi->fs_func->get_entry_type((DENTRY_T *) dos_ep); + + /* Check dos_ep */ + if (!strncmp(dos_ep->name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + ; + } else if (!strncmp(dos_ep->name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + ; + } else if ((type == TYPE_DIR) || (type == TYPE_FILE)) { + + /* Set start_clus */ + SET32_HI(start_clus, le16_to_cpu(dos_ep->start_clu_hi)); + SET32_LO(start_clus, le16_to_cpu(dos_ep->start_clu_lo)); + arg->start_clus = start_clus; + + /* Set type & i_pos */ + if (type == TYPE_DIR) + arg->type = DFR_TRAV_TYPE_DIR; + else + arg->type = DFR_TRAV_TYPE_FILE; + + arg->i_pos = i_pos; + + /* Set name */ + memset(&uniname, 0, sizeof(UNI_NAME_T)); + get_uniname_from_dos_entry(sb, dos_ep, &uniname, 0x1); + /* FIXME : + * we should think that whether the size of arg->name + * is enough or not + */ + nls_uni16s_to_vfsname(sb, &uniname, + arg->name, sizeof(arg->name)); + + err = 0; + /* End case */ + } else if (type == TYPE_UNUSED) { + err = -ENOENT; + } else { + ; + } + +error: + return err; +} + + +/** + * @fn defrag_scan_dir + * @brief scan given directory + * @return 0 on success, -errno otherwise + * @param sb super block + * @param args traverse args + * @remark protected by inode_lock, super_block and volume lock + */ +int +defrag_scan_dir( + IN struct super_block *sb, + INOUT struct defrag_trav_arg *args) +{ + struct sdfat_sb_info *sbi = NULL; + FS_INFO_T *fsi = NULL; + struct defrag_trav_header *header = NULL; + DOS_DENTRY_T *dos_ep; + CHAIN_T chain; + int dot_found = 0, args_idx = DFR_TRAV_HEADER_IDX + 1, clus = 0, index = 0; + int err = 0, j = 0; + + /* Check params */ + ERR_HANDLE2((!sb || !args), err, -EINVAL); + sbi = SDFAT_SB(sb); + fsi = &(sbi->fsi); + header = (struct defrag_trav_header *) args; + + /* Exceptional case for ROOT */ + if (header->i_pos == DFR_TRAV_ROOT_IPOS) { + header->start_clus = fsi->root_dir; + dfr_debug("IOC_DFR_TRAV for ROOT: start_clus %08x", header->start_clus); + dot_found = 1; + } + + chain.dir = header->start_clus; + chain.size = 0; + chain.flags = 0; + + /* Check if this is directory */ + if (!dot_found) { + FAT32_CHECK_CLUSTER(fsi, chain.dir, err); + ERR_HANDLE(err); + dos_ep = (DOS_DENTRY_T *) get_dentry_in_dir(sb, &chain, 0, NULL); + ERR_HANDLE2(!dos_ep, err, -EIO); + + if (strncmp(dos_ep->name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + err = -EINVAL; + dfr_err("Scan: Not a directory, err %d", err); + goto error; + } + } + + /* For more-scan case */ + if ((header->stat == DFR_TRAV_STAT_MORE) && + (header->start_clus == sbi->dfr_hint_clus) && + (sbi->dfr_hint_idx > 0)) { + + index = sbi->dfr_hint_idx; + for (j = 0; j < (sbi->dfr_hint_idx / fsi->dentries_per_clu); j++) { + /* Follow FAT-chain */ + FAT32_CHECK_CLUSTER(fsi, chain.dir, err); + ERR_HANDLE(err); + err = fat_ent_get(sb, chain.dir, &(chain.dir)); + ERR_HANDLE(err); + + if (!IS_CLUS_EOF(chain.dir)) { + clus++; + index -= fsi->dentries_per_clu; + } else { + /** + * This directory modified. Stop scanning. + */ + err = -EINVAL; + dfr_err("Scan: SCAN_MORE failed, err %d", err); + goto error; + } + } + + /* For first-scan case */ + } else { + clus = 0; + index = 0; + } + +scan_fat_chain: + /* Scan given directory and get info of children */ + for ( ; index < fsi->dentries_per_clu; index++) { + DOS_DENTRY_T *dos_ep = NULL; + loff_t i_pos = 0; + + /* Get dos_ep */ + FAT32_CHECK_CLUSTER(fsi, chain.dir, err); + ERR_HANDLE(err); + dos_ep = (DOS_DENTRY_T *) get_dentry_in_dir(sb, &chain, index, NULL); + ERR_HANDLE2(!dos_ep, err, -EIO); + + /* Make i_pos for this entry */ + SET64_HI(i_pos, header->start_clus); + SET64_LO(i_pos, clus * fsi->dentries_per_clu + index); + + err = __defrag_scan_dir(sb, dos_ep, i_pos, &args[args_idx]); + if (!err) { + /* More-scan case */ + if (++args_idx >= (PAGE_SIZE / sizeof(struct defrag_trav_arg))) { + sbi->dfr_hint_clus = header->start_clus; + sbi->dfr_hint_idx = clus * fsi->dentries_per_clu + index + 1; + + header->stat = DFR_TRAV_STAT_MORE; + header->nr_entries = args_idx; + goto error; + } + /* Error case */ + } else if (err == -EINVAL) { + sbi->dfr_hint_clus = sbi->dfr_hint_idx = 0; + dfr_err("Scan: err %d", err); + goto error; + /* End case */ + } else if (err == -ENOENT) { + sbi->dfr_hint_clus = sbi->dfr_hint_idx = 0; + err = 0; + goto done; + } else { + /* DO NOTHING */ + } + err = 0; + } + + /* Follow FAT-chain */ + FAT32_CHECK_CLUSTER(fsi, chain.dir, err); + ERR_HANDLE(err); + err = fat_ent_get(sb, chain.dir, &(chain.dir)); + ERR_HANDLE(err); + + if (!IS_CLUS_EOF(chain.dir)) { + index = 0; + clus++; + goto scan_fat_chain; + } + +done: + /* Update header */ + header->stat = DFR_TRAV_STAT_DONE; + header->nr_entries = args_idx; + +error: + return err; +} + + +static int +__defrag_validate_cluster_prev( + IN struct super_block *sb, + IN struct defrag_chunk_info *chunk) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + CHAIN_T dir; + DENTRY_T *ep = NULL; + unsigned int entry = 0, clus = 0; + int err = 0; + + if (chunk->prev_clus == 0) { + /* For the first cluster of a file */ + dir.dir = GET64_HI(chunk->i_pos); + dir.flags = 0x1; // Assume non-continuous + + entry = GET64_LO(chunk->i_pos); + + FAT32_CHECK_CLUSTER(fsi, dir.dir, err); + ERR_HANDLE(err); + ep = get_dentry_in_dir(sb, &dir, entry, NULL); + if (!ep) { + err = -EPERM; + goto error; + } + + /* should call fat_get_entry_clu0(ep) */ + clus = fsi->fs_func->get_entry_clu0(ep); + if (clus != chunk->d_clus) { + err = -ENXIO; + goto error; + } + } else { + /* Normal case */ + FAT32_CHECK_CLUSTER(fsi, chunk->prev_clus, err); + ERR_HANDLE(err); + err = fat_ent_get(sb, chunk->prev_clus, &clus); + if (err) + goto error; + if (chunk->d_clus != clus) + err = -ENXIO; + } + +error: + return err; +} + + +static int +__defrag_validate_cluster_next( + IN struct super_block *sb, + IN struct defrag_chunk_info *chunk) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + unsigned int clus = 0; + int err = 0; + + /* Check next_clus */ + FAT32_CHECK_CLUSTER(fsi, (chunk->d_clus + chunk->nr_clus - 1), err); + ERR_HANDLE(err); + err = fat_ent_get(sb, (chunk->d_clus + chunk->nr_clus - 1), &clus); + if (err) + goto error; + if (chunk->next_clus != (clus & FAT32_EOF)) + err = -ENXIO; + +error: + return err; +} + + +/** + * @fn __defrag_check_au + * @brief check if this AU is in use + * @return 0 if idle, 1 if busy + * @param sb super block + * @param clus physical cluster num + * @param limit # of used clusters from daemon + */ +static int +__defrag_check_au( + struct super_block *sb, + u32 clus, + u32 limit) +{ + unsigned int nr_free = amap_get_freeclus(sb, clus); + +#if defined(CONFIG_SDFAT_DFR_DEBUG) && defined(CONFIG_SDFAT_DBG_MSG) + if (nr_free < limit) { + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + AU_INFO_T *au = GET_AU(amap, i_AU_of_CLU(amap, clus)); + + dfr_debug("AU[%d] nr_free %d, limit %d", au->idx, nr_free, limit); + } +#endif + return ((nr_free < limit) ? 1 : 0); +} + + +/** + * @fn defrag_validate_cluster + * @brief validate cluster info of given chunk + * @return 0 on success, -errno otherwise + * @param inode inode of given chunk + * @param chunk given chunk + * @param skip_prev flag to skip checking previous cluster info + * @remark protected by super_block and volume lock + */ +int +defrag_validate_cluster( + IN struct inode *inode, + IN struct defrag_chunk_info *chunk, + IN int skip_prev) +{ + struct super_block *sb = inode->i_sb; + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + unsigned int clus = 0; + int err = 0, i = 0; + + /* If this inode is unlink-ed, skip it */ + if (fid->dir.dir == DIR_DELETED) + return -ENOENT; + + /* Skip working-AU */ + err = amap_check_working(sb, chunk->d_clus); + if (err) + return -EBUSY; + + /* Check # of free_clus of belonged AU */ + err = __defrag_check_au(inode->i_sb, chunk->d_clus, CLUS_PER_AU(sb) - chunk->au_clus); + if (err) + return -EINVAL; + + /* Check chunk's clusters */ + for (i = 0; i < chunk->nr_clus; i++) { + err = fsapi_map_clus(inode, chunk->f_clus + i, &clus, ALLOC_NOWHERE); + if (err || (chunk->d_clus + i != clus)) { + if (!err) + err = -ENXIO; + goto error; + } + } + + /* Check next_clus */ + err = __defrag_validate_cluster_next(sb, chunk); + ERR_HANDLE(err); + + if (!skip_prev) { + /* Check prev_clus */ + err = __defrag_validate_cluster_prev(sb, chunk); + ERR_HANDLE(err); + } + +error: + return err; +} + + +/** + * @fn defrag_reserve_clusters + * @brief reserve clusters for defrag + * @return 0 on success, -errno otherwise + * @param sb super block + * @param nr_clus # of clusters to reserve + * @remark protected by super_block and volume lock + */ +int +defrag_reserve_clusters( + INOUT struct super_block *sb, + IN int nr_clus) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + + if (!(sbi->options.improved_allocation & SDFAT_ALLOC_DELAY)) + /* Nothing to do */ + return 0; + + /* Check error case */ + if (fsi->used_clusters + fsi->reserved_clusters + nr_clus >= fsi->num_clusters - 2) { + return -ENOSPC; + } else if (fsi->reserved_clusters + nr_clus < 0) { + dfr_err("Reserve count: reserved_clusters %d, nr_clus %d", + fsi->reserved_clusters, nr_clus); + BUG_ON(fsi->reserved_clusters + nr_clus < 0); + } + + sbi->dfr_reserved_clus += nr_clus; + fsi->reserved_clusters += nr_clus; + + return 0; +} + + +/** + * @fn defrag_mark_ignore + * @brief mark corresponding AU to be ignored + * @return 0 on success, -errno otherwise + * @param sb super block + * @param clus given cluster num + * @remark protected by super_block + */ +int +defrag_mark_ignore( + INOUT struct super_block *sb, + IN unsigned int clus) +{ + int err = 0; + + if (SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_SMART) + err = amap_mark_ignore(sb, clus); + + if (err) + dfr_debug("err %d", err); + return err; +} + + +/** + * @fn defrag_unmark_ignore_all + * @brief unmark all ignored AUs + * @return void + * @param sb super block + * @remark protected by super_block + */ +void +defrag_unmark_ignore_all(struct super_block *sb) +{ + if (SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_SMART) + amap_unmark_ignore_all(sb); +} + + +/** + * @fn defrag_map_cluster + * @brief get_block function for defrag dests + * @return 0 on success, -errno otherwise + * @param inode inode + * @param clu_offset logical cluster offset + * @param clu mapped cluster (physical) + * @remark protected by super_block and volume lock + */ +int +defrag_map_cluster( + struct inode *inode, + unsigned int clu_offset, + unsigned int *clu) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); +#ifdef CONFIG_SDFAT_DFR_PACKING + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; +#endif + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + struct defrag_info *ino_dfr = &(SDFAT_I(inode)->dfr_info); + struct defrag_chunk_info *chunk = NULL; + CHAIN_T new_clu; + int i = 0, nr_new = 0, err = 0; + + /* Get corresponding chunk */ + for (i = 0; i < ino_dfr->nr_chunks; i++) { + chunk = &(ino_dfr->chunks[i]); + + if ((chunk->f_clus <= clu_offset) && (clu_offset < chunk->f_clus + chunk->nr_clus)) { + /* For already allocated new_clus */ + if (sbi->dfr_new_clus[chunk->new_idx + clu_offset - chunk->f_clus]) { + *clu = sbi->dfr_new_clus[chunk->new_idx + clu_offset - chunk->f_clus]; + return 0; + } + break; + } + } + BUG_ON(!chunk); + + fscore_set_vol_flags(sb, VOL_DIRTY, 0); + + new_clu.dir = CLUS_EOF; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* Allocate new cluster */ +#ifdef CONFIG_SDFAT_DFR_PACKING + if (amap->n_clean_au * DFR_FULL_RATIO <= amap->n_au * DFR_DEFAULT_PACKING_RATIO) + err = fsi->fs_func->alloc_cluster(sb, 1, &new_clu, ALLOC_COLD_PACKING); + else + err = fsi->fs_func->alloc_cluster(sb, 1, &new_clu, ALLOC_COLD_ALIGNED); +#else + err = fsi->fs_func->alloc_cluster(sb, 1, &new_clu, ALLOC_COLD_ALIGNED); +#endif + + if (err) { + dfr_err("Map: 1 %d", 0); + return err; + } + + /* Decrease reserved cluster count */ + defrag_reserve_clusters(sb, -1); + + /* Add new_clus info in ino_dfr */ + sbi->dfr_new_clus[chunk->new_idx + clu_offset - chunk->f_clus] = new_clu.dir; + + /* Make FAT-chain for new_clus */ + for (i = 0; i < chunk->nr_clus; i++) { +#if 0 + if (sbi->dfr_new_clus[chunk->new_idx + i]) + nr_new++; + else + break; +#else + if (!sbi->dfr_new_clus[chunk->new_idx + i]) + break; + nr_new++; +#endif + } + if (nr_new == chunk->nr_clus) { + for (i = 0; i < chunk->nr_clus - 1; i++) { + FAT32_CHECK_CLUSTER(fsi, sbi->dfr_new_clus[chunk->new_idx + i], err); + BUG_ON(err); + if (fat_ent_set(sb, + sbi->dfr_new_clus[chunk->new_idx + i], + sbi->dfr_new_clus[chunk->new_idx + i + 1])) + return -EIO; + } + } + + *clu = new_clu.dir; + return 0; +} + + +/** + * @fn defrag_writepage_end_io + * @brief check WB status of requested page + * @return void + * @param page page + */ +void +defrag_writepage_end_io( + INOUT struct page *page) +{ + struct super_block *sb = page->mapping->host->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct defrag_info *ino_dfr = &(SDFAT_I(page->mapping->host)->dfr_info); + unsigned int clus_start = 0, clus_end = 0; + int i = 0; + + /* Check if this inode is on defrag */ + if (atomic_read(&ino_dfr->stat) != DFR_INO_STAT_REQ) + return; + + clus_start = page->index / PAGES_PER_CLUS(sb); + clus_end = clus_start + 1; + + /* Check each chunk in given inode */ + for (i = 0; i < ino_dfr->nr_chunks; i++) { + struct defrag_chunk_info *chunk = &(ino_dfr->chunks[i]); + unsigned int chunk_start = 0, chunk_end = 0; + + chunk_start = chunk->f_clus; + chunk_end = chunk->f_clus + chunk->nr_clus; + + if ((clus_start >= chunk_start) && (clus_end <= chunk_end)) { + int off = clus_start - chunk_start; + + clear_bit((page->index & (PAGES_PER_CLUS(sb) - 1)), + (volatile unsigned long *)&(sbi->dfr_page_wb[chunk->new_idx + off])); + } + } +} + + +/** + * @fn __defrag_check_wb + * @brief check if WB for given chunk completed + * @return 0 on success, -errno otherwise + * @param sbi super block info + * @param chunk given chunk + */ +static int +__defrag_check_wb( + IN struct sdfat_sb_info *sbi, + IN struct defrag_chunk_info *chunk) +{ + int err = 0, wb_i = 0, i = 0, nr_new = 0; + + if (!sbi || !chunk) + return -EINVAL; + + /* Check WB complete status first */ + for (wb_i = 0; wb_i < chunk->nr_clus; wb_i++) { + if (atomic_read((atomic_t *)&(sbi->dfr_page_wb[chunk->new_idx + wb_i]))) { + err = -EBUSY; + break; + } + } + + /** + * Check NEW_CLUS status. + * writepage_end_io cannot check whole WB complete status, + * so we need to check NEW_CLUS status. + */ + for (i = 0; i < chunk->nr_clus; i++) + if (sbi->dfr_new_clus[chunk->new_idx + i]) + nr_new++; + + if (nr_new == chunk->nr_clus) { + err = 0; + if ((wb_i != chunk->nr_clus) && (wb_i != chunk->nr_clus - 1)) + dfr_debug("submit_fullpage_bio() called on a page (nr_clus %d, wb_i %d)", + chunk->nr_clus, wb_i); + + BUG_ON(nr_new > chunk->nr_clus); + } else { + dfr_debug("nr_new %d, nr_clus %d", nr_new, chunk->nr_clus); + err = -EBUSY; + } + + /* Update chunk's state */ + if (!err) + chunk->stat |= DFR_CHUNK_STAT_WB; + + return err; +} + + +static void +__defrag_check_fat_old( + IN struct super_block *sb, + IN struct inode *inode, + IN struct defrag_chunk_info *chunk) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + unsigned int clus = 0; + int err = 0, idx = 0, max_idx = 0; + + /* Get start_clus */ + clus = SDFAT_I(inode)->fid.start_clu; + + /* Follow FAT-chain */ + #define num_clusters(val) ((val) ? (s32)((val - 1) >> fsi->cluster_size_bits) + 1 : 0) + max_idx = num_clusters(SDFAT_I(inode)->i_size_ondisk); + for (idx = 0; idx < max_idx; idx++) { + + FAT32_CHECK_CLUSTER(fsi, clus, err); + ERR_HANDLE(err); + err = fat_ent_get(sb, clus, &clus); + ERR_HANDLE(err); + + if ((idx < max_idx - 1) && (IS_CLUS_EOF(clus) || IS_CLUS_FREE(clus))) { + dfr_err("FAT: inode %p, max_idx %d, idx %d, clus %08x, " + "f_clus %d, nr_clus %d", inode, max_idx, + idx, clus, chunk->f_clus, chunk->nr_clus); + BUG_ON(idx < max_idx - 1); + goto error; + } + } + +error: + return; +} + + +static void +__defrag_check_fat_new( + IN struct super_block *sb, + IN struct inode *inode, + IN struct defrag_chunk_info *chunk) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + unsigned int clus = 0; + int i = 0, err = 0; + + /* Check start of FAT-chain */ + if (chunk->prev_clus) { + FAT32_CHECK_CLUSTER(fsi, chunk->prev_clus, err); + BUG_ON(err); + err = fat_ent_get(sb, chunk->prev_clus, &clus); + BUG_ON(err); + } else { + clus = SDFAT_I(inode)->fid.start_clu; + } + if (sbi->dfr_new_clus[chunk->new_idx] != clus) { + dfr_err("FAT: inode %p, start_clus %08x, read_clus %08x", + inode, sbi->dfr_new_clus[chunk->new_idx], clus); + err = EIO; + goto error; + } + + /* Check inside of FAT-chain */ + if (chunk->nr_clus > 1) { + for (i = 0; i < chunk->nr_clus - 1; i++) { + FAT32_CHECK_CLUSTER(fsi, sbi->dfr_new_clus[chunk->new_idx + i], err); + BUG_ON(err); + err = fat_ent_get(sb, sbi->dfr_new_clus[chunk->new_idx + i], &clus); + BUG_ON(err); + if (sbi->dfr_new_clus[chunk->new_idx + i + 1] != clus) { + dfr_err("FAT: inode %p, new_clus %08x, read_clus %08x", + inode, sbi->dfr_new_clus[chunk->new_idx], clus); + err = EIO; + goto error; + } + } + clus = 0; + } + + /* Check end of FAT-chain */ + FAT32_CHECK_CLUSTER(fsi, sbi->dfr_new_clus[chunk->new_idx + chunk->nr_clus - 1], err); + BUG_ON(err); + err = fat_ent_get(sb, sbi->dfr_new_clus[chunk->new_idx + chunk->nr_clus - 1], &clus); + BUG_ON(err); + if ((chunk->next_clus & 0x0FFFFFFF) != (clus & 0x0FFFFFFF)) { + dfr_err("FAT: inode %p, next_clus %08x, read_clus %08x", inode, chunk->next_clus, clus); + err = EIO; + } + +error: + BUG_ON(err); +} + + +/** + * @fn __defrag_update_dirent + * @brief update DIR entry for defrag req + * @return void + * @param sb super block + * @param chunk given chunk + */ +static void +__defrag_update_dirent( + struct super_block *sb, + struct defrag_chunk_info *chunk) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &SDFAT_SB(sb)->fsi; + CHAIN_T dir; + DOS_DENTRY_T *dos_ep; + unsigned int entry = 0; + unsigned long long sector = 0; + unsigned short hi = 0, lo = 0; + int err = 0; + + dir.dir = GET64_HI(chunk->i_pos); + dir.flags = 0x1; // Assume non-continuous + + entry = GET64_LO(chunk->i_pos); + + FAT32_CHECK_CLUSTER(fsi, dir.dir, err); + BUG_ON(err); + dos_ep = (DOS_DENTRY_T *) get_dentry_in_dir(sb, &dir, entry, §or); + + hi = GET32_HI(sbi->dfr_new_clus[chunk->new_idx]); + lo = GET32_LO(sbi->dfr_new_clus[chunk->new_idx]); + + dos_ep->start_clu_hi = cpu_to_le16(hi); + dos_ep->start_clu_lo = cpu_to_le16(lo); + + dcache_modify(sb, sector); +} + + +/** + * @fn defrag_update_fat_prev + * @brief update FAT chain for defrag requests + * @return void + * @param sb super block + * @param force flag to force FAT update + * @remark protected by super_block and volume lock + */ +void +defrag_update_fat_prev( + struct super_block *sb, + int force) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + struct defrag_info *sb_dfr = &sbi->dfr_info, *ino_dfr = NULL; + int skip = 0, done = 0; + + /* Check if FS_ERROR occurred */ + if (sb->s_flags & MS_RDONLY) { + dfr_err("RDONLY partition (err %d)", -EPERM); + goto out; + } + + list_for_each_entry(ino_dfr, &sb_dfr->entry, entry) { + struct inode *inode = &(container_of(ino_dfr, struct sdfat_inode_info, dfr_info)->vfs_inode); + struct sdfat_inode_info *ino_info = SDFAT_I(inode); + struct defrag_chunk_info *chunk_prev = NULL; + int i = 0, j = 0; + + mutex_lock(&ino_dfr->lock); + BUG_ON(atomic_read(&ino_dfr->stat) != DFR_INO_STAT_REQ); + for (i = 0; i < ino_dfr->nr_chunks; i++) { + struct defrag_chunk_info *chunk = NULL; + int err = 0; + + chunk = &(ino_dfr->chunks[i]); + BUG_ON(!chunk); + + /* Do nothing for already passed chunk */ + if (chunk->stat == DFR_CHUNK_STAT_PASS) { + done++; + continue; + } + + /* Handle error case */ + if (chunk->stat == DFR_CHUNK_STAT_ERR) { + err = -EINVAL; + goto error; + } + + /* Double-check clusters */ + if (chunk_prev && + (chunk->f_clus == chunk_prev->f_clus + chunk_prev->nr_clus) && + (chunk_prev->stat == DFR_CHUNK_STAT_PASS)) { + + err = defrag_validate_cluster(inode, chunk, 1); + + /* Handle continuous chunks in a file */ + if (!err) { + chunk->prev_clus = + sbi->dfr_new_clus[chunk_prev->new_idx + chunk_prev->nr_clus - 1]; + dfr_debug("prev->f_clus %d, prev->nr_clus %d, chunk->f_clus %d", + chunk_prev->f_clus, chunk_prev->nr_clus, chunk->f_clus); + } + } else { + err = defrag_validate_cluster(inode, chunk, 0); + } + + if (err) { + dfr_err("Cluster validation: inode %p, chunk->f_clus %d, err %d", + inode, chunk->f_clus, err); + goto error; + } + + /** + * Skip update_fat_prev if WB or update_fat_next not completed. + * Go to error case if FORCE set. + */ + if (__defrag_check_wb(sbi, chunk) || (chunk->stat != DFR_CHUNK_STAT_PREP)) { + if (force) { + err = -EPERM; + dfr_err("Skip case: inode %p, stat %x, f_clus %d, err %d", + inode, chunk->stat, chunk->f_clus, err); + goto error; + } + skip++; + continue; + } + +#ifdef CONFIG_SDFAT_DFR_DEBUG + /* SPO test */ + defrag_spo_test(sb, DFR_SPO_RANDOM, __func__); +#endif + + /* Update chunk's previous cluster */ + if (chunk->prev_clus == 0) { + /* For the first cluster of a file */ + /* Update ino_info->fid.start_clu */ + ino_info->fid.start_clu = sbi->dfr_new_clus[chunk->new_idx]; + __defrag_update_dirent(sb, chunk); + } else { + FAT32_CHECK_CLUSTER(fsi, chunk->prev_clus, err); + BUG_ON(err); + if (fat_ent_set(sb, + chunk->prev_clus, + sbi->dfr_new_clus[chunk->new_idx])) { + err = -EIO; + goto error; + } + } + + /* Clear extent cache */ + extent_cache_inval_inode(inode); + + /* Update FID info */ + ino_info->fid.hint_bmap.off = CLUS_EOF; + ino_info->fid.hint_bmap.clu = 0; + + /* Clear old FAT-chain */ + for (j = 0; j < chunk->nr_clus; j++) + defrag_free_cluster(sb, chunk->d_clus + j); + + /* Mark this chunk PASS */ + chunk->stat = DFR_CHUNK_STAT_PASS; + __defrag_check_fat_new(sb, inode, chunk); + + done++; + +error: + if (err) { + /** + * chunk->new_idx != 0 means this chunk needs to be cleaned up + */ + if (chunk->new_idx) { + /* Free already allocated clusters */ + for (j = 0; j < chunk->nr_clus; j++) { + if (sbi->dfr_new_clus[chunk->new_idx + j]) { + defrag_free_cluster(sb, sbi->dfr_new_clus[chunk->new_idx + j]); + sbi->dfr_new_clus[chunk->new_idx + j] = 0; + } + } + + __defrag_check_fat_old(sb, inode, chunk); + } + + /** + * chunk->new_idx == 0 means this chunk already cleaned up + */ + chunk->new_idx = 0; + chunk->stat = DFR_CHUNK_STAT_ERR; + } + + chunk_prev = chunk; + } + BUG_ON(!mutex_is_locked(&ino_dfr->lock)); + mutex_unlock(&ino_dfr->lock); + } + +out: + if (skip) { + dfr_debug("%s skipped (nr_reqs %d, done %d, skip %d)", + __func__, sb_dfr->nr_chunks - 1, done, skip); + } else { + /* Make dfr_reserved_clus zero */ + if (sbi->dfr_reserved_clus > 0) { + if (fsi->reserved_clusters < sbi->dfr_reserved_clus) { + dfr_err("Reserved count: reserved_clus %d, dfr_reserved_clus %d", + fsi->reserved_clusters, sbi->dfr_reserved_clus); + BUG_ON(fsi->reserved_clusters < sbi->dfr_reserved_clus); + } + + defrag_reserve_clusters(sb, 0 - sbi->dfr_reserved_clus); + } + + dfr_debug("%s done (nr_reqs %d, done %d)", __func__, sb_dfr->nr_chunks - 1, done); + } +} + + +/** + * @fn defrag_update_fat_next + * @brief update FAT chain for defrag requests + * @return void + * @param sb super block + * @remark protected by super_block and volume lock + */ +void +defrag_update_fat_next( + struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + struct defrag_info *sb_dfr = &sbi->dfr_info, *ino_dfr = NULL; + struct defrag_chunk_info *chunk = NULL; + int done = 0, i = 0, j = 0, err = 0; + + /* Check if FS_ERROR occurred */ + if (sb->s_flags & MS_RDONLY) { + dfr_err("RDONLY partition (err %d)", -EROFS); + goto out; + } + + list_for_each_entry(ino_dfr, &sb_dfr->entry, entry) { + + for (i = 0; i < ino_dfr->nr_chunks; i++) { + int skip = 0; + + chunk = &(ino_dfr->chunks[i]); + + /* Do nothing if error occurred or update_fat_next already passed */ + if (chunk->stat == DFR_CHUNK_STAT_ERR) + continue; + if (chunk->stat & DFR_CHUNK_STAT_FAT) { + done++; + continue; + } + + /* Ship this chunk if get_block not passed for this chunk */ + for (j = 0; j < chunk->nr_clus; j++) { + if (sbi->dfr_new_clus[chunk->new_idx + j] == 0) { + skip = 1; + break; + } + } + if (skip) + continue; + + /* Update chunk's next cluster */ + FAT32_CHECK_CLUSTER(fsi, + sbi->dfr_new_clus[chunk->new_idx + chunk->nr_clus - 1], err); + BUG_ON(err); + if (fat_ent_set(sb, + sbi->dfr_new_clus[chunk->new_idx + chunk->nr_clus - 1], + chunk->next_clus)) + goto out; + +#ifdef CONFIG_SDFAT_DFR_DEBUG + /* SPO test */ + defrag_spo_test(sb, DFR_SPO_RANDOM, __func__); +#endif + + /* Update chunk's state */ + chunk->stat |= DFR_CHUNK_STAT_FAT; + done++; + } + } + +out: + dfr_debug("%s done (nr_reqs %d, done %d)", __func__, sb_dfr->nr_chunks - 1, done); +} + + +/** + * @fn defrag_check_discard + * @brief check if we can send discard for this AU, if so, send discard + * @return void + * @param sb super block + * @remark protected by super_block and volume lock + */ +void +defrag_check_discard( + IN struct super_block *sb) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + AMAP_T *amap = SDFAT_SB(sb)->fsi.amap; + AU_INFO_T *au = NULL; + struct defrag_info *sb_dfr = &(SDFAT_SB(sb)->dfr_info); + unsigned int tmp[DFR_MAX_AU_MOVED]; + int i = 0, j = 0; + + BUG_ON(!amap); + + if (!(SDFAT_SB(sb)->options.discard) || + !(SDFAT_SB(sb)->options.improved_allocation & SDFAT_ALLOC_SMART)) + return; + + memset(tmp, 0, sizeof(int) * DFR_MAX_AU_MOVED); + + for (i = REQ_HEADER_IDX + 1; i < sb_dfr->nr_chunks; i++) { + struct defrag_chunk_info *chunk = &(sb_dfr->chunks[i]); + int skip = 0; + + au = GET_AU(amap, i_AU_of_CLU(amap, chunk->d_clus)); + + /* Send DISCARD for free AU */ + if ((IS_AU_IGNORED(au, amap)) && + (amap_get_freeclus(sb, chunk->d_clus) == CLUS_PER_AU(sb))) { + sector_t blk = 0, nr_blks = 0; + unsigned int au_align_factor = amap->option.au_align_factor % amap->option.au_size; + + BUG_ON(au->idx == 0); + + /* Avoid multiple DISCARD */ + for (j = 0; j < DFR_MAX_AU_MOVED; j++) { + if (tmp[j] == au->idx) { + skip = 1; + break; + } + } + if (skip == 1) + continue; + + /* Send DISCARD cmd */ + blk = (sector_t) (((au->idx * CLUS_PER_AU(sb)) << fsi->sect_per_clus_bits) + - au_align_factor); + nr_blks = ((sector_t)CLUS_PER_AU(sb)) << fsi->sect_per_clus_bits; + + dfr_debug("Send DISCARD for AU[%d] (blk %08zx)", au->idx, blk); + sb_issue_discard(sb, blk, nr_blks, GFP_NOFS, 0); + + /* Save previous AU's index */ + for (j = 0; j < DFR_MAX_AU_MOVED; j++) { + if (!tmp[j]) { + tmp[j] = au->idx; + break; + } + } + } + } +} + + +/** + * @fn defrag_free_cluster + * @brief free uneccessary cluster + * @return void + * @param sb super block + * @param clus physical cluster num + * @remark protected by super_block and volume lock + */ +int +defrag_free_cluster( + struct super_block *sb, + unsigned int clus) +{ + FS_INFO_T *fsi = &SDFAT_SB(sb)->fsi; + unsigned int val = 0; + s32 err = 0; + + FAT32_CHECK_CLUSTER(fsi, clus, err); + BUG_ON(err); + if (fat_ent_get(sb, clus, &val)) + return -EIO; + if (val) { + if (fat_ent_set(sb, clus, 0)) + return -EIO; + } else { + dfr_err("Free: Already freed, clus %08x, val %08x", clus, val); + BUG_ON(!val); + } + + set_sb_dirty(sb); + fsi->used_clusters--; + if (fsi->amap) + amap_release_cluster(sb, clus); + + return 0; +} + + +/** + * @fn defrag_check_defrag_required + * @brief check if defrag required + * @return 1 if required, 0 otherwise + * @param sb super block + * @param totalau # of total AUs + * @param cleanau # of clean AUs + * @param fullau # of full AUs + * @remark protected by super_block + */ +int +defrag_check_defrag_required( + IN struct super_block *sb, + OUT int *totalau, + OUT int *cleanau, + OUT int *fullau) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + AMAP_T *amap = NULL; + int clean_ratio = 0, frag_ratio = 0; + int ret = 0; + + if (!sb || !(SDFAT_SB(sb)->options.defrag)) + return 0; + + /* Check DFR_DEFAULT_STOP_RATIO first */ + fsi = &(SDFAT_SB(sb)->fsi); + if (fsi->used_clusters == (unsigned int)(~0)) { + if (fsi->fs_func->count_used_clusters(sb, &fsi->used_clusters)) + return -EIO; + } + if (fsi->used_clusters * DFR_FULL_RATIO >= fsi->num_clusters * DFR_DEFAULT_STOP_RATIO) { + dfr_debug("used_clusters %d, num_clusters %d", fsi->used_clusters, fsi->num_clusters); + return 0; + } + + /* Check clean/frag ratio */ + amap = SDFAT_SB(sb)->fsi.amap; + BUG_ON(!amap); + + clean_ratio = (amap->n_clean_au * 100) / amap->n_au; + if (amap->n_full_au) + frag_ratio = ((amap->n_au - amap->n_clean_au) * 100) / amap->n_full_au; + else + frag_ratio = ((amap->n_au - amap->n_clean_au) * 100) / + (fsi->used_clusters * CLUS_PER_AU(sb)); + + /* + * Wake-up defrag_daemon: + * when # of clean AUs too small, or frag_ratio exceeds the limit + */ + if ((clean_ratio < DFR_DEFAULT_WAKEUP_RATIO) || + ((clean_ratio < DFR_DEFAULT_CLEAN_RATIO) && (frag_ratio >= DFR_DEFAULT_FRAG_RATIO))) { + + if (totalau) + *totalau = amap->n_au; + if (cleanau) + *cleanau = amap->n_clean_au; + if (fullau) + *fullau = amap->n_full_au; + ret = 1; + } + + return ret; +} + + +/** + * @fn defrag_check_defrag_required + * @brief check defrag status on inode + * @return 1 if defrag in on, 0 otherwise + * @param inode inode + * @param start logical start addr + * @param end logical end addr + * @param cancel flag to cancel defrag + * @param caller caller info + */ +int +defrag_check_defrag_on( + INOUT struct inode *inode, + IN loff_t start, + IN loff_t end, + IN int cancel, + IN const char *caller) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + struct defrag_info *ino_dfr = &(SDFAT_I(inode)->dfr_info); + unsigned int clus_start = 0, clus_end = 0; + int ret = 0, i = 0; + + if (!inode || (start == end)) + return 0; + + mutex_lock(&ino_dfr->lock); + /* Check if this inode is on defrag */ + if (atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) { + + clus_start = start >> (fsi->cluster_size_bits); + clus_end = (end >> (fsi->cluster_size_bits)) + + ((end & (fsi->cluster_size - 1)) ? 1 : 0); + + if (!ino_dfr->chunks) + goto error; + + /* Check each chunk in given inode */ + for (i = 0; i < ino_dfr->nr_chunks; i++) { + struct defrag_chunk_info *chunk = &(ino_dfr->chunks[i]); + unsigned int chunk_start = 0, chunk_end = 0; + + /* Skip this chunk when error occurred or it already passed defrag process */ + if ((chunk->stat == DFR_CHUNK_STAT_ERR) || (chunk->stat == DFR_CHUNK_STAT_PASS)) + continue; + + chunk_start = chunk->f_clus; + chunk_end = chunk->f_clus + chunk->nr_clus; + + if (((clus_start >= chunk_start) && (clus_start < chunk_end)) || + ((clus_end > chunk_start) && (clus_end <= chunk_end)) || + ((clus_start < chunk_start) && (clus_end > chunk_end))) { + ret = 1; + if (cancel) { + chunk->stat = DFR_CHUNK_STAT_ERR; + dfr_debug("Defrag canceled: inode %p, start %08x, end %08x, caller %s", + inode, clus_start, clus_end, caller); + } + } + } + } + +error: + BUG_ON(!mutex_is_locked(&ino_dfr->lock)); + mutex_unlock(&ino_dfr->lock); + return ret; +} + + +#ifdef CONFIG_SDFAT_DFR_DEBUG +/** + * @fn defrag_spo_test + * @brief test SPO while defrag running + * @return void + * @param sb super block + * @param flag SPO debug flag + * @param caller caller info + */ +void +defrag_spo_test( + struct super_block *sb, + int flag, + const char *caller) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + if (!sb || !(SDFAT_SB(sb)->options.defrag)) + return; + + if (flag == sbi->dfr_spo_flag) { + dfr_err("Defrag SPO test (flag %d, caller %s)", flag, caller); + panic("Defrag SPO test"); + } +} +#endif /* CONFIG_SDFAT_DFR_DEBUG */ + + +#endif /* CONFIG_SDFAT_DFR */ diff --git a/fs/sdfat/dfr.h b/fs/sdfat/dfr.h new file mode 100644 index 0000000000000..da98605020d34 --- /dev/null +++ b/fs/sdfat/dfr.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _SDFAT_DEFRAG_H +#define _SDFAT_DEFRAG_H + +#ifdef CONFIG_SDFAT_DFR + +/* Tuning parameters */ +#define DFR_MIN_TIMEOUT (1 * HZ) // Minimum timeout for forced-sync +#define DFR_DEFAULT_TIMEOUT (10 * HZ) // Default timeout for forced-sync + +#define DFR_DEFAULT_CLEAN_RATIO (50) // Wake-up daemon when clean AU ratio under 50% +#define DFR_DEFAULT_WAKEUP_RATIO (10) // Wake-up daemon when clean AU ratio under 10%, regardless of frag_ratio + +#define DFR_DEFAULT_FRAG_RATIO (130) // Wake-up daemon when frag_ratio over 130% + +#define DFR_DEFAULT_PACKING_RATIO (10) // Call allocator with PACKING flag, when clean AU ratio under 10% + +#define DFR_DEFAULT_STOP_RATIO (98) // Stop defrag_daemon when disk used ratio over 98% +#define DFR_FULL_RATIO (100) + +#define DFR_MAX_AU_MOVED (16) // Maximum # of AUs for a request + + +/* Debugging support*/ +#define dfr_err(fmt, args...) pr_err("DFR: " fmt "\n", args) + +#ifdef CONFIG_SDFAT_DFR_DEBUG +#define dfr_debug(fmt, args...) pr_debug("DFR: " fmt "\n", args) +#else +#define dfr_debug(fmt, args...) +#endif + + +/* Error handling */ +#define ERR_HANDLE(err) { \ + if (err) { \ + dfr_debug("err %d", err); \ + goto error; \ + } \ +} + +#define ERR_HANDLE2(cond, err, val) { \ + if (cond) { \ + err = val; \ + dfr_debug("err %d", err); \ + goto error; \ + } \ +} + + +/* Arguments IN-OUT */ +#define IN +#define OUT +#define INOUT + + +/* Macros */ +#define GET64_HI(var64) ((unsigned int)((var64) >> 32)) +#define GET64_LO(var64) ((unsigned int)(((var64) << 32) >> 32)) +#define SET64_HI(dst64, var32) { (dst64) = ((loff_t)(var32) << 32) | ((dst64) & 0x00000000ffffffffLL); } +#define SET64_LO(dst64, var32) { (dst64) = ((dst64) & 0xffffffff00000000LL) | ((var32) & 0x00000000ffffffffLL); } + +#define GET32_HI(var32) ((unsigned short)((var32) >> 16)) +#define GET32_LO(var32) ((unsigned short)(((var32) << 16) >> 16)) +#define SET32_HI(dst32, var16) { (dst32) = ((unsigned int)(var16) << 16) | ((dst32) & 0x0000ffff); } +#define SET32_LO(dst32, var16) { (dst32) = ((dst32) & 0xffff0000) | ((unsigned int)(var16) & 0x0000ffff); } + + +/* FAT32 related */ +#define FAT32_EOF (0x0fffffff) +#define FAT32_RESERVED (0x0ffffff7) +#define FAT32_UNUSED_CLUS (2) + +#define CLUS_PER_AU(sb) ( \ + (SDFAT_SB(sb)->options.amap_opt.sect_per_au) >> (SDFAT_SB(sb)->fsi.sect_per_clus_bits) \ +) +#define PAGES_PER_AU(sb) ( \ + ((SDFAT_SB(sb)->options.amap_opt.sect_per_au) << ((sb)->s_blocksize_bits)) \ + >> PAGE_SHIFT \ +) +#define PAGES_PER_CLUS(sb) ((SDFAT_SB(sb)->fsi.cluster_size) >> PAGE_SHIFT) + +#define FAT32_CHECK_CLUSTER(fsi, clus, err) \ + { \ + if (((clus) < FAT32_UNUSED_CLUS) || \ + ((clus) > (fsi)->num_clusters) || \ + ((clus) >= FAT32_RESERVED)) { \ + dfr_err("clus %08x, fsi->num_clusters %08x", (clus), (fsi)->num_clusters); \ + err = -EINVAL; \ + } else { \ + err = 0; \ + } \ + } + + +/* IOCTL_DFR_INFO */ +struct defrag_info_arg { + /* PBS info */ + unsigned int sec_sz; + unsigned int clus_sz; + unsigned long long total_sec; + unsigned long long fat_offset_sec; + unsigned int fat_sz_sec; + unsigned int n_fat; + unsigned int hidden_sectors; + + /* AU info */ + unsigned int sec_per_au; +}; + + +/* IOC_DFR_TRAV */ +#define DFR_TRAV_HEADER_IDX (0) + +#define DFR_TRAV_TYPE_HEADER (0x0000000F) +#define DFR_TRAV_TYPE_DIR (1) +#define DFR_TRAV_TYPE_FILE (2) +#define DFR_TRAV_TYPE_TEST (DFR_TRAV_TYPE_HEADER | 0x10000000) + +#define DFR_TRAV_ROOT_IPOS (0xFFFFFFFFFFFFFFFFLL) + +struct defrag_trav_arg { + int type; + unsigned int start_clus; + loff_t i_pos; + char name[MAX_DOSNAME_BUF_SIZE]; + char dummy1; + int dummy2; +}; + +#define DFR_TRAV_STAT_DONE (0x1) +#define DFR_TRAV_STAT_MORE (0x2) +#define DFR_TRAV_STAT_ERR (0xFF) + +struct defrag_trav_header { + int type; + unsigned int start_clus; + loff_t i_pos; + char name[MAX_DOSNAME_BUF_SIZE]; + char stat; + unsigned int nr_entries; +}; + + +/* IOC_DFR_REQ */ +#define REQ_HEADER_IDX (0) + +#define DFR_CHUNK_STAT_ERR (0xFFFFFFFF) +#define DFR_CHUNK_STAT_REQ (0x1) +#define DFR_CHUNK_STAT_WB (0x2) +#define DFR_CHUNK_STAT_FAT (0x4) +#define DFR_CHUNK_STAT_PREP (DFR_CHUNK_STAT_REQ | DFR_CHUNK_STAT_WB | DFR_CHUNK_STAT_FAT) +#define DFR_CHUNK_STAT_PASS (0x0000000F) + +struct defrag_chunk_header { + int mode; + unsigned int nr_chunks; + loff_t dummy1; + int dummy2[4]; + union { + int *dummy3; + int dummy4; + }; + int dummy5; +}; + +struct defrag_chunk_info { + int stat; + /* File related */ + unsigned int f_clus; + loff_t i_pos; + /* Cluster related */ + unsigned int d_clus; + unsigned int nr_clus; + unsigned int prev_clus; + unsigned int next_clus; + union { + void *dummy; + /* req status */ + unsigned int new_idx; + }; + /* AU related */ + unsigned int au_clus; +}; + + +/* Global info */ +#define DFR_MODE_BACKGROUND (0x1) +#define DFR_MODE_FOREGROUND (0x2) +#define DFR_MODE_ONESHOT (0x4) +#define DFR_MODE_BATCHED (0x8) +#define DFR_MODE_TEST (DFR_MODE_BACKGROUND | 0x10000000) + +#define DFR_SB_STAT_IDLE (0) +#define DFR_SB_STAT_REQ (1) +#define DFR_SB_STAT_VALID (2) + +#define DFR_INO_STAT_IDLE (0) +#define DFR_INO_STAT_REQ (1) +struct defrag_info { + struct mutex lock; + atomic_t stat; + struct defrag_chunk_info *chunks; + unsigned int nr_chunks; + struct list_head entry; +}; + + +/* SPO test flags */ +#define DFR_SPO_NONE (0) +#define DFR_SPO_NORMAL (1) +#define DFR_SPO_DISCARD (2) +#define DFR_SPO_FAT_NEXT (3) +#define DFR_SPO_RANDOM (4) + + +/* Extern functions */ +int defrag_get_info(struct super_block *sb, struct defrag_info_arg *arg); + +int defrag_scan_dir(struct super_block *sb, struct defrag_trav_arg *arg); + +int defrag_validate_cluster(struct inode *inode, struct defrag_chunk_info *chunk, int skip_prev); +int defrag_reserve_clusters(struct super_block *sb, int nr_clus); +int defrag_mark_ignore(struct super_block *sb, unsigned int clus); +void defrag_unmark_ignore_all(struct super_block *sb); + +int defrag_map_cluster(struct inode *inode, unsigned int clu_offset, unsigned int *clu); +void defrag_writepage_end_io(struct page *page); + +void defrag_update_fat_prev(struct super_block *sb, int force); +void defrag_update_fat_next(struct super_block *sb); +void defrag_check_discard(struct super_block *sb); +int defrag_free_cluster(struct super_block *sb, unsigned int clus); + +int defrag_check_defrag_required(struct super_block *sb, int *totalau, int *cleanau, int *fullau); +int defrag_check_defrag_on(struct inode *inode, loff_t start, loff_t end, int cancel, const char *caller); + +#ifdef CONFIG_SDFAT_DFR_DEBUG +void defrag_spo_test(struct super_block *sb, int flag, const char *caller); +#endif + +#endif /* CONFIG_SDFAT_DFR */ + +#endif /* _SDFAT_DEFRAG_H */ + diff --git a/fs/sdfat/extent.c b/fs/sdfat/extent.c new file mode 100644 index 0000000000000..59e096530ddad --- /dev/null +++ b/fs/sdfat/extent.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * linux/fs/fat/cache.c + * + * Written 1992,1993 by Werner Almesberger + * + * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead + * of inode number. + * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : extent.c */ +/* PURPOSE : Improve the performance of traversing fat chain. */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include "sdfat.h" +#include "core.h" + +#define EXTENT_CACHE_VALID 0 +/* this must be > 0. */ +#define EXTENT_MAX_CACHE 16 + +struct extent_cache { + struct list_head cache_list; + u32 nr_contig; /* number of contiguous clusters */ + u32 fcluster; /* cluster number in the file. */ + u32 dcluster; /* cluster number on disk. */ +}; + +struct extent_cache_id { + u32 id; + u32 nr_contig; + u32 fcluster; + u32 dcluster; +}; + +static struct kmem_cache *extent_cache_cachep; + +static void init_once(void *c) +{ + struct extent_cache *cache = (struct extent_cache *)c; + + INIT_LIST_HEAD(&cache->cache_list); +} + +s32 extent_cache_init(void) +{ + extent_cache_cachep = kmem_cache_create("sdfat_extent_cache", + sizeof(struct extent_cache), + 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, + init_once); + if (!extent_cache_cachep) + return -ENOMEM; + return 0; +} + +void extent_cache_shutdown(void) +{ + if (!extent_cache_cachep) + return; + kmem_cache_destroy(extent_cache_cachep); +} + +void extent_cache_init_inode(struct inode *inode) +{ + EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent); + + spin_lock_init(&extent->cache_lru_lock); + extent->nr_caches = 0; + extent->cache_valid_id = EXTENT_CACHE_VALID + 1; + INIT_LIST_HEAD(&extent->cache_lru); +} + +static inline struct extent_cache *extent_cache_alloc(void) +{ + return kmem_cache_alloc(extent_cache_cachep, GFP_NOFS); +} + +static inline void extent_cache_free(struct extent_cache *cache) +{ + BUG_ON(!list_empty(&cache->cache_list)); + kmem_cache_free(extent_cache_cachep, cache); +} + +static inline void extent_cache_update_lru(struct inode *inode, + struct extent_cache *cache) +{ + EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent); + + if (extent->cache_lru.next != &cache->cache_list) + list_move(&cache->cache_list, &extent->cache_lru); +} + +static u32 extent_cache_lookup(struct inode *inode, u32 fclus, + struct extent_cache_id *cid, + u32 *cached_fclus, u32 *cached_dclus) +{ + EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent); + + static struct extent_cache nohit = { .fcluster = 0, }; + + struct extent_cache *hit = &nohit, *p; + u32 offset = CLUS_EOF; + + spin_lock(&extent->cache_lru_lock); + list_for_each_entry(p, &extent->cache_lru, cache_list) { + /* Find the cache of "fclus" or nearest cache. */ + if (p->fcluster <= fclus && hit->fcluster < p->fcluster) { + hit = p; + if ((hit->fcluster + hit->nr_contig) < fclus) { + offset = hit->nr_contig; + } else { + offset = fclus - hit->fcluster; + break; + } + } + } + if (hit != &nohit) { + extent_cache_update_lru(inode, hit); + + cid->id = extent->cache_valid_id; + cid->nr_contig = hit->nr_contig; + cid->fcluster = hit->fcluster; + cid->dcluster = hit->dcluster; + *cached_fclus = cid->fcluster + offset; + *cached_dclus = cid->dcluster + offset; + } + spin_unlock(&extent->cache_lru_lock); + + return offset; +} + +static struct extent_cache *extent_cache_merge(struct inode *inode, + struct extent_cache_id *new) +{ + EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent); + + struct extent_cache *p; + + list_for_each_entry(p, &extent->cache_lru, cache_list) { + /* Find the same part as "new" in cluster-chain. */ + if (p->fcluster == new->fcluster) { + ASSERT(p->dcluster == new->dcluster); + if (new->nr_contig > p->nr_contig) + p->nr_contig = new->nr_contig; + return p; + } + } + return NULL; +} + +static void extent_cache_add(struct inode *inode, struct extent_cache_id *new) +{ + EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent); + + struct extent_cache *cache, *tmp; + + if (new->fcluster == -1) /* dummy cache */ + return; + + spin_lock(&extent->cache_lru_lock); + if (new->id != EXTENT_CACHE_VALID && + new->id != extent->cache_valid_id) + goto out; /* this cache was invalidated */ + + cache = extent_cache_merge(inode, new); + if (cache == NULL) { + if (extent->nr_caches < EXTENT_MAX_CACHE) { + extent->nr_caches++; + spin_unlock(&extent->cache_lru_lock); + + tmp = extent_cache_alloc(); + if (!tmp) { + spin_lock(&extent->cache_lru_lock); + extent->nr_caches--; + spin_unlock(&extent->cache_lru_lock); + return; + } + + spin_lock(&extent->cache_lru_lock); + cache = extent_cache_merge(inode, new); + if (cache != NULL) { + extent->nr_caches--; + extent_cache_free(tmp); + goto out_update_lru; + } + cache = tmp; + } else { + struct list_head *p = extent->cache_lru.prev; + cache = list_entry(p, struct extent_cache, cache_list); + } + cache->fcluster = new->fcluster; + cache->dcluster = new->dcluster; + cache->nr_contig = new->nr_contig; + } +out_update_lru: + extent_cache_update_lru(inode, cache); +out: + spin_unlock(&extent->cache_lru_lock); +} + +/* + * Cache invalidation occurs rarely, thus the LRU chain is not updated. It + * fixes itself after a while. + */ +static void __extent_cache_inval_inode(struct inode *inode) +{ + EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent); + struct extent_cache *cache; + + while (!list_empty(&extent->cache_lru)) { + cache = list_entry(extent->cache_lru.next, + struct extent_cache, cache_list); + list_del_init(&cache->cache_list); + extent->nr_caches--; + extent_cache_free(cache); + } + /* Update. The copy of caches before this id is discarded. */ + extent->cache_valid_id++; + if (extent->cache_valid_id == EXTENT_CACHE_VALID) + extent->cache_valid_id++; +} + +void extent_cache_inval_inode(struct inode *inode) +{ + EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent); + + spin_lock(&extent->cache_lru_lock); + __extent_cache_inval_inode(inode); + spin_unlock(&extent->cache_lru_lock); +} + +static inline s32 cache_contiguous(struct extent_cache_id *cid, u32 dclus) +{ + cid->nr_contig++; + return ((cid->dcluster + cid->nr_contig) == dclus); +} + +static inline void cache_init(struct extent_cache_id *cid, u32 fclus, u32 dclus) +{ + cid->id = EXTENT_CACHE_VALID; + cid->fcluster = fclus; + cid->dcluster = dclus; + cid->nr_contig = 0; +} + +s32 extent_get_clus(struct inode *inode, u32 cluster, u32 *fclus, + u32 *dclus, u32 *last_dclus, s32 allow_eof) +{ + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + u32 limit = fsi->num_clusters; + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + struct extent_cache_id cid; + u32 content; + + /* FOR GRACEFUL ERROR HANDLING */ + if (IS_CLUS_FREE(fid->start_clu)) { + sdfat_fs_error(sb, "invalid access to " + "extent cache (entry 0x%08x)", fid->start_clu); + ASSERT(0); + return -EIO; + } + + *fclus = 0; + *dclus = fid->start_clu; + *last_dclus = *dclus; + + /* + * Don`t use extent_cache if zero offset or non-cluster allocation + */ + if ((cluster == 0) || IS_CLUS_EOF(*dclus)) + return 0; + + cache_init(&cid, CLUS_EOF, CLUS_EOF); + + if (extent_cache_lookup(inode, cluster, &cid, fclus, dclus) == CLUS_EOF) { + /* + * dummy, always not contiguous + * This is reinitialized by cache_init(), later. + */ + ASSERT((cid.id == EXTENT_CACHE_VALID) + && (cid.fcluster == CLUS_EOF) + && (cid.dcluster == CLUS_EOF) + && (cid.nr_contig == 0)); + } + + if (*fclus == cluster) + return 0; + + while (*fclus < cluster) { + /* prevent the infinite loop of cluster chain */ + if (*fclus > limit) { + sdfat_fs_error(sb, + "%s: detected the cluster chain loop" + " (i_pos %u)", __func__, + (*fclus)); + return -EIO; + } + + if (fat_ent_get_safe(sb, *dclus, &content)) + return -EIO; + + *last_dclus = *dclus; + *dclus = content; + (*fclus)++; + + if (IS_CLUS_EOF(content)) { + if (!allow_eof) { + sdfat_fs_error(sb, + "%s: invalid cluster chain (i_pos %u," + "last_clus 0x%08x is EOF)", + __func__, *fclus, (*last_dclus)); + return -EIO; + } + + break; + } + + if (!cache_contiguous(&cid, *dclus)) + cache_init(&cid, *fclus, *dclus); + } + + extent_cache_add(inode, &cid); + return 0; +} diff --git a/fs/sdfat/fatent.c b/fs/sdfat/fatent.c new file mode 100644 index 0000000000000..fca32a50d3363 --- /dev/null +++ b/fs/sdfat/fatent.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : fatent.c */ +/* PURPOSE : sdFAT FAT entry manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include + +#include "sdfat.h" +#include "core.h" + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ +/* All buffer structures are protected w/ fsi->v_sem */ + +/*----------------------------------------------------------------------*/ +/* Static functions */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* FAT Read/Write Functions */ +/*======================================================================*/ +/* in : sb, loc + * out: content + * returns 0 on success, -1 on error + */ +static s32 exfat_ent_get(struct super_block *sb, u32 loc, u32 *content) +{ + u32 off, _content; + u64 sec; + u8 *fat_sector; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + /* fsi->vol_type == EXFAT */ + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + _content = le32_to_cpu(*(__le32 *)(&fat_sector[off])); + + /* remap reserved clusters to simplify code */ + if (_content >= CLUSTER_32(0xFFFFFFF8)) + _content = CLUS_EOF; + + *content = CLUSTER_32(_content); + return 0; +} + +static s32 exfat_ent_set(struct super_block *sb, u32 loc, u32 content) +{ + u32 off; + u64 sec; + u8 *fat_sector; + __le32 *fat_entry; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + fat_entry = (__le32 *)&(fat_sector[off]); + *fat_entry = cpu_to_le32(content); + + return fcache_modify(sb, sec); +} + +#define FATENT_FAT32_VALID_MASK (0x0FFFFFFFU) +#define FATENT_FAT32_IGNORE_MASK (0xF0000000U) +static s32 fat32_ent_get(struct super_block *sb, u32 loc, u32 *content) +{ + u32 off, _content; + u64 sec; + u8 *fat_sector; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + _content = le32_to_cpu(*(__le32 *)(&fat_sector[off])); + _content &= FATENT_FAT32_VALID_MASK; + + /* remap reserved clusters to simplify code */ + if (_content == CLUSTER_32(0x0FFFFFF7U)) + _content = CLUS_BAD; + else if (_content >= CLUSTER_32(0x0FFFFFF8U)) + _content = CLUS_EOF; + + *content = CLUSTER_32(_content); + return 0; +} + +static s32 fat32_ent_set(struct super_block *sb, u32 loc, u32 content) +{ + u32 off; + u64 sec; + u8 *fat_sector; + __le32 *fat_entry; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + content &= FATENT_FAT32_VALID_MASK; + + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + fat_entry = (__le32 *)&(fat_sector[off]); + content |= (le32_to_cpu(*fat_entry) & FATENT_FAT32_IGNORE_MASK); + *fat_entry = cpu_to_le32(content); + + return fcache_modify(sb, sec); +} + +#define FATENT_FAT16_VALID_MASK (0x0000FFFFU) +static s32 fat16_ent_get(struct super_block *sb, u32 loc, u32 *content) +{ + u32 off, _content; + u64 sec; + u8 *fat_sector; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1)); + off = (loc << 1) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + _content = (u32)le16_to_cpu(*(__le16 *)(&fat_sector[off])); + _content &= FATENT_FAT16_VALID_MASK; + + /* remap reserved clusters to simplify code */ + if (_content == CLUSTER_16(0xFFF7U)) + _content = CLUS_BAD; + else if (_content >= CLUSTER_16(0xFFF8U)) + _content = CLUS_EOF; + + *content = CLUSTER_32(_content); + return 0; +} + +static s32 fat16_ent_set(struct super_block *sb, u32 loc, u32 content) +{ + u32 off; + u64 sec; + u8 *fat_sector; + __le16 *fat_entry; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + content &= FATENT_FAT16_VALID_MASK; + + sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1)); + off = (loc << 1) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + fat_entry = (__le16 *)&(fat_sector[off]); + *fat_entry = cpu_to_le16(content); + + return fcache_modify(sb, sec); +} + +#define FATENT_FAT12_VALID_MASK (0x00000FFFU) +static s32 fat12_ent_get(struct super_block *sb, u32 loc, u32 *content) +{ + u32 off, _content; + u64 sec; + u8 *fat_sector; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits); + off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + if (off == (u32)(sb->s_blocksize - 1)) { + _content = (u32) fat_sector[off]; + + fat_sector = fcache_getblk(sb, ++sec); + if (!fat_sector) + return -EIO; + + _content |= (u32) fat_sector[0] << 8; + } else { + _content = get_unaligned_le16(&fat_sector[off]); + } + + if (loc & 1) + _content >>= 4; + + _content &= FATENT_FAT12_VALID_MASK; + + /* remap reserved clusters to simplify code */ + if (_content == CLUSTER_16(0x0FF7U)) + _content = CLUS_BAD; + else if (_content >= CLUSTER_16(0x0FF8U)) + _content = CLUS_EOF; + + *content = CLUSTER_32(_content); + return 0; +} + +static s32 fat12_ent_set(struct super_block *sb, u32 loc, u32 content) +{ + u32 off; + u64 sec; + u8 *fat_sector, *fat_entry; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + content &= FATENT_FAT12_VALID_MASK; + + sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits); + off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1); + + fat_sector = fcache_getblk(sb, sec); + if (!fat_sector) + return -EIO; + + if (loc & 1) { /* odd */ + + content <<= 4; + + if (off == (u32)(sb->s_blocksize-1)) { + fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F)); + if (fcache_modify(sb, sec)) + return -EIO; + + fat_sector = fcache_getblk(sb, ++sec); + if (!fat_sector) + return -EIO; + + fat_sector[0] = (u8)(content >> 8); + } else { + fat_entry = &(fat_sector[off]); + content |= 0x000F & get_unaligned_le16(fat_entry); + put_unaligned_le16(content, fat_entry); + } + } else { /* even */ + fat_sector[off] = (u8)(content); + + if (off == (u32)(sb->s_blocksize-1)) { + fat_sector[off] = (u8)(content); + if (fcache_modify(sb, sec)) + return -EIO; + + fat_sector = fcache_getblk(sb, ++sec); + if (!fat_sector) + return -EIO; + + fat_sector[0] = (u8)((fat_sector[0] & 0xF0) | (content >> 8)); + } else { + fat_entry = &(fat_sector[off]); + content |= 0xF000 & get_unaligned_le16(fat_entry); + put_unaligned_le16(content, fat_entry); + } + } + return fcache_modify(sb, sec); +} + + +static FATENT_OPS_T fat12_ent_ops = { + fat12_ent_get, + fat12_ent_set +}; + +static FATENT_OPS_T fat16_ent_ops = { + fat16_ent_get, + fat16_ent_set +}; + +static FATENT_OPS_T fat32_ent_ops = { + fat32_ent_get, + fat32_ent_set +}; + +static FATENT_OPS_T exfat_ent_ops = { + exfat_ent_get, + exfat_ent_set +}; + +s32 fat_ent_ops_init(struct super_block *sb) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + switch (fsi->vol_type) { + case EXFAT: + fsi->fatent_ops = &exfat_ent_ops; + break; + case FAT32: + fsi->fatent_ops = &fat32_ent_ops; + break; + case FAT16: + fsi->fatent_ops = &fat16_ent_ops; + break; + case FAT12: + fsi->fatent_ops = &fat12_ent_ops; + break; + default: + fsi->fatent_ops = NULL; + EMSG("Unknown volume type : %d", (int)fsi->vol_type); + return -ENOTSUPP; + } + + return 0; +} + +static inline bool is_reserved_clus(u32 clus) +{ + if (IS_CLUS_FREE(clus)) + return true; + if (IS_CLUS_EOF(clus)) + return true; + if (IS_CLUS_BAD(clus)) + return true; + return false; +} + +static inline bool is_valid_clus(FS_INFO_T *fsi, u32 clus) +{ + if (clus < CLUS_BASE || fsi->num_clusters <= clus) + return false; + return true; +} + +s32 fat_ent_get(struct super_block *sb, u32 loc, u32 *content) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + s32 err; + + if (!is_valid_clus(fsi, loc)) { + sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", loc); + return -EIO; + } + + err = fsi->fatent_ops->ent_get(sb, loc, content); + if (err) { + sdfat_fs_error(sb, "failed to access to FAT " + "(entry 0x%08x, err:%d)", loc, err); + return err; + } + + if (!is_reserved_clus(*content) && !is_valid_clus(fsi, *content)) { + sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x) " + "bogus content (0x%08x)", loc, *content); + return -EIO; + } + + return 0; +} + +s32 fat_ent_set(struct super_block *sb, u32 loc, u32 content) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + return fsi->fatent_ops->ent_set(sb, loc, content); +} + +s32 fat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content) +{ + s32 err = fat_ent_get(sb, loc, content); + + if (err) + return err; + + if (IS_CLUS_FREE(*content)) { + sdfat_fs_error(sb, "invalid access to FAT free cluster " + "(entry 0x%08x)", loc); + return -EIO; + } + + if (IS_CLUS_BAD(*content)) { + sdfat_fs_error(sb, "invalid access to FAT bad cluster " + "(entry 0x%08x)", loc); + return -EIO; + } + + return 0; +} + +/* end of fatent.c */ diff --git a/fs/sdfat/misc.c b/fs/sdfat/misc.c new file mode 100644 index 0000000000000..b9592ef4ca98f --- /dev/null +++ b/fs/sdfat/misc.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : misc.c */ +/* PURPOSE : Helper function for checksum and handing sdFAT error */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include +#include "sdfat.h" +#include "version.h" + +#ifdef CONFIG_SDFAT_SUPPORT_STLOG +#include +#else +#define ST_LOG(fmt, ...) +#endif + +/* + * sdfat_fs_error reports a file system problem that might indicate fa data + * corruption/inconsistency. Depending on 'errors' mount option the + * panic() is called, or error message is printed FAT and nothing is done, + * or filesystem is remounted read-only (default behavior). + * In case the file system is remounted read-only, it can be made writable + * again by remounting it. + */ +void __sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) +{ + struct sdfat_mount_options *opts = &SDFAT_SB(sb)->options; + va_list args; + struct va_format vaf; + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + if (report) { + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + pr_err("[SDFAT](%s[%d:%d]):ERR: %pV\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf); +#ifdef CONFIG_SDFAT_SUPPORT_STLOG + if (opts->errors == SDFAT_ERRORS_RO && !(sb->s_flags & MS_RDONLY)) { + ST_LOG("[SDFAT](%s[%d:%d]):ERR: %pV\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf); + } +#endif + va_end(args); + } + + if (opts->errors == SDFAT_ERRORS_PANIC) { + panic("[SDFAT](%s[%d:%d]): fs panic from previous error\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); + } else if (opts->errors == SDFAT_ERRORS_RO && !(sb->s_flags & MS_RDONLY)) { + sb->s_flags |= MS_RDONLY; + pr_err("[SDFAT](%s[%d:%d]): Filesystem has been set " + "read-only\n", sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); +#ifdef CONFIG_SDFAT_SUPPORT_STLOG + ST_LOG("[SDFAT](%s[%d:%d]): Filesystem has been set read-only\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); +#endif + } +} +EXPORT_SYMBOL(__sdfat_fs_error); + +/** + * __sdfat_msg() - print preformated SDFAT specific messages. + * All logs except what uses sdfat_fs_error() should be written by __sdfat_msg() + * If 'st' is set, the log is propagated to ST_LOG. + */ +void __sdfat_msg(struct super_block *sb, const char *level, int st, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + /* level means KERN_ pacility level */ + printk("%s[SDFAT](%s[%d:%d]): %pV\n", level, + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf); +#ifdef CONFIG_SDFAT_SUPPORT_STLOG + if (st) { + ST_LOG("[SDFAT](%s[%d:%d]): %pV\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf); + } +#endif + va_end(args); +} +EXPORT_SYMBOL(__sdfat_msg); + +void sdfat_log_version(void) +{ + pr_info("[SDFAT] Filesystem version %s\n", SDFAT_VERSION); +#ifdef CONFIG_SDFAT_SUPPORT_STLOG + ST_LOG("[SDFAT] Filesystem version %s\n", SDFAT_VERSION); +#endif +} +EXPORT_SYMBOL(sdfat_log_version); + +/* externs sys_tz + * extern struct timezone sys_tz; + */ +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif + +/* days between 1970/01/01 and 1980/01/01 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + /* 2100 isn't leap year */ \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while (0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Month : N 01 02 03 04 05 06 07 08 09 10 11 12 */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ +void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t year = tp->Year; + time_t ld; /* leap day */ + + MAKE_LEAP_YEAR(ld, year); + + if (IS_LEAP_YEAR(year) && (tp->Month) > 2) + ld++; + + ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN + + tp->Hour * SECS_PER_HOUR + + (year * 365 + ld + accum_days_in_year[tp->Month] + + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY; + + if (!sbi->options.tz_utc) + ts->tv_sec += sys_tz.tz_minuteswest * SECS_PER_MIN; + + ts->tv_nsec = 0; +} + +/* Convert linear UNIX date to a FAT time/date pair. */ +void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t second = ts->tv_sec; + time_t day, month, year; + time_t ld; /* leap day */ + + if (!sbi->options.tz_utc) + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->Second = 0; + tp->Minute = 0; + tp->Hour = 0; + tp->Day = 1; + tp->Month = 1; + tp->Year = 0; + return; + } +#if (BITS_PER_LONG == 64) + if (second >= UNIX_SECS_2108) { + tp->Second = 59; + tp->Minute = 59; + tp->Hour = 23; + tp->Day = 31; + tp->Month = 12; + tp->Year = 127; + return; + } +#endif + + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + + MAKE_LEAP_YEAR(ld, year); + if (year * 365 + ld > day) + year--; + + MAKE_LEAP_YEAR(ld, year); + day -= year * 365 + ld; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->Second = second % SECS_PER_MIN; + tp->Minute = (second / SECS_PER_MIN) % 60; + tp->Hour = (second / SECS_PER_HOUR) % 24; + tp->Day = day + 1; + tp->Month = month; + tp->Year = year; +} + +TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tp) +{ + struct timespec ts; + DATE_TIME_T dt; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) + ts = CURRENT_TIME_SEC; +#else + ktime_get_real_ts(&ts); +#endif + + sdfat_time_unix2fat(sbi, &ts, &dt); + + tp->year = dt.Year; + tp->mon = dt.Month; + tp->day = dt.Day; + tp->hour = dt.Hour; + tp->min = dt.Minute; + tp->sec = dt.Second; + + return tp; +} + +u8 calc_chksum_1byte(void *data, s32 len, u8 chksum) +{ + s32 i; + u8 *c = (u8 *) data; + + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c; + + return chksum; +} + +u16 calc_chksum_2byte(void *data, s32 len, u16 chksum, s32 type) +{ + s32 i; + u8 *c = (u8 *) data; + + for (i = 0; i < len; i++, c++) { + if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY)) + continue; + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c; + } + return chksum; +} + +#ifdef CONFIG_SDFAT_TRACE_ELAPSED_TIME +struct timeval __t1, __t2; +u32 sdfat_time_current_usec(struct timeval *tv) +{ + do_gettimeofday(tv); + return (u32)(tv->tv_sec*1000000 + tv->tv_usec); +} +#endif /* CONFIG_SDFAT_TRACE_ELAPSED_TIME */ + +#ifdef CONFIG_SDFAT_DBG_CAREFUL +/* Check the consistency of i_size_ondisk (FAT32, or flags 0x01 only) */ +void sdfat_debug_check_clusters(struct inode *inode) +{ + unsigned int num_clusters; + volatile uint32_t tmp_fat_chain[50]; + volatile int tmp_i = 0; + volatile unsigned int num_clusters_org, tmp_i = 0; + CHAIN_T clu; + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + FS_INFO_T *fsi = &(SDFAT_SB(inode->i_sb)->fsi); + + if (SDFAT_I(inode)->i_size_ondisk == 0) + num_clusters = 0; + else + num_clusters = ((SDFAT_I(inode)->i_size_ondisk-1) >> fsi->cluster_size_bits) + 1; + + clu.dir = fid->start_clu; + clu.size = num_clusters; + clu.flags = fid->flags; + + num_clusters_org = num_clusters; + + if (clu.flags == 0x03) + return; + + while (num_clusters > 0) { + /* FAT chain logging */ + tmp_fat_chain[tmp_i] = clu.dir; + tmp_i++; + if (tmp_i >= 50) + tmp_i = 0; + + BUG_ON(IS_CLUS_EOF(clu.dir) || IS_CLUS_FREE(clu.dir)); + + if (get_next_clus_safe(inode->i_sb, &(clu.dir))) + EMSG("%s: failed to access to FAT\n"); + + num_clusters--; + } + + BUG_ON(!IS_CLUS_EOF(clu.dir)); +} + +#endif /* CONFIG_SDFAT_DBG_CAREFUL */ + +#ifdef CONFIG_SDFAT_DBG_MSG +void __sdfat_dmsg(int level, const char *fmt, ...) +{ +#ifdef CONFIG_SDFAT_DBG_SHOW_PID + struct va_format vaf; + va_list args; + + /* should check type */ + if (level > SDFAT_MSG_LEVEL) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + /* fmt already includes KERN_ pacility level */ + printk("[%u] %pV", current->pid, &vaf); + va_end(args); +#else + va_list args; + + /* should check type */ + if (level > SDFAT_MSG_LEVEL) + return; + + va_start(args, fmt); + /* fmt already includes KERN_ pacility level */ + vprintk(fmt, args); + va_end(args); +#endif +} +#endif diff --git a/fs/sdfat/mpage.c b/fs/sdfat/mpage.c new file mode 100644 index 0000000000000..b3985825815b6 --- /dev/null +++ b/fs/sdfat/mpage.c @@ -0,0 +1,622 @@ +/* + * fs/mpage.c + * + * Copyright (C) 2002, Linus Torvalds. + * + * Contains functions related to preparing and submitting BIOs which contain + * multiple pagecache pages. + * + * 15May2002 Andrew Morton + * Initial version + * 27Jun2002 axboe@suse.de + * use bio_add_page() to build bio's just the right size + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : core.c */ +/* PURPOSE : sdFAT glue layer for supporting VFS */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mark_page_accessed() */ +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#include +#endif + +#include "sdfat.h" + +#ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE + +/************************************************************************* + * INNER FUNCTIONS FOR FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +static void __mpage_write_end_io(struct bio *bio, int err); + +/************************************************************************* + * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) +static inline void __sdfat_submit_bio_write2(int flags, struct bio *bio) +{ + bio_set_op_attrs(bio, REQ_OP_WRITE, flags); + submit_bio(bio); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) */ +static inline void __sdfat_submit_bio_write2(int flags, struct bio *bio) +{ + submit_bio(WRITE | flags, bio); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) +static void mpage_write_end_io(struct bio *bio) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + __mpage_write_end_io(bio, bio->bi_status); +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) */ + __mpage_write_end_io(bio, bio->bi_error); +#endif +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0) */ +static void mpage_write_end_io(struct bio *bio, int err) +{ + if (test_bit(BIO_UPTODATE, &bio->bi_flags)) + err = 0; + __mpage_write_end_io(bio, err); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +static inline int bio_get_nr_vecs(struct block_device *bdev) +{ + return BIO_MAX_PAGES; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) */ + /* EMPTY */ +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static inline sector_t __sdfat_bio_sector(struct bio *bio) +{ + return bio->bi_iter.bi_sector; +} + +static inline void __sdfat_set_bio_sector(struct bio *bio, sector_t sector) +{ + bio->bi_iter.bi_sector = sector; +} + +static inline unsigned int __sdfat_bio_size(struct bio *bio) +{ + return bio->bi_iter.bi_size; +} + +static inline void __sdfat_set_bio_size(struct bio *bio, unsigned int size) +{ + bio->bi_iter.bi_size = size; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */ +static inline sector_t __sdfat_bio_sector(struct bio *bio) +{ + return bio->bi_sector; +} + +static inline void __sdfat_set_bio_sector(struct bio *bio, sector_t sector) +{ + bio->bi_sector = sector; +} + +static inline unsigned int __sdfat_bio_size(struct bio *bio) +{ + return bio->bi_size; +} + +static inline void __sdfat_set_bio_size(struct bio *bio, unsigned int size) +{ + bio->bi_size = size; +} +#endif + +/* __check_dfr_on() and __dfr_writepage_end_io() functions + * are copied from sdfat.c + * Each function should be same perfectly + */ +static inline int __check_dfr_on(struct inode *inode, loff_t start, loff_t end, const char *fname) +{ +#ifdef CONFIG_SDFAT_DFR + struct defrag_info *ino_dfr = &(SDFAT_I(inode)->dfr_info); + + if ((atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) && + fsapi_dfr_check_dfr_on(inode, start, end, 0, fname)) + return 1; +#endif + return 0; +} + +static inline int __dfr_writepage_end_io(struct page *page) +{ +#ifdef CONFIG_SDFAT_DFR + struct defrag_info *ino_dfr = &(SDFAT_I(page->mapping->host)->dfr_info); + + if (atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) + fsapi_dfr_writepage_endio(page); +#endif + return 0; +} + + +static inline unsigned int __calc_size_to_align(struct super_block *sb) +{ + struct block_device *bdev = sb->s_bdev; + struct gendisk *disk; + struct request_queue *queue; + struct queue_limits *limit; + unsigned int max_sectors; + unsigned int aligned = 0; + + disk = bdev->bd_disk; + if (!disk) + goto out; + + queue = disk->queue; + if (!queue) + goto out; + + limit = &queue->limits; + max_sectors = limit->max_sectors; + aligned = 1 << ilog2(max_sectors); + + if (aligned && (max_sectors & (aligned - 1))) + aligned = 0; +out: + return aligned; +} + +struct mpage_data { + struct bio *bio; + sector_t last_block_in_bio; + get_block_t *get_block; + unsigned int use_writepage; + unsigned int size_to_align; +}; + +/* + * I/O completion handler for multipage BIOs. + * + * The mpage code never puts partial pages into a BIO (except for end-of-file). + * If a page does not map to a contiguous run of blocks then it simply falls + * back to block_read_full_page(). + * + * Why is this? If a page's completion depends on a number of different BIOs + * which can complete in any order (or at the same time) then determining the + * status of that page is hard. See end_buffer_async_read() for the details. + * There is no point in duplicating all that complexity. + */ +static void __mpage_write_end_io(struct bio *bio, int err) +{ + struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; + + ASSERT(bio_data_dir(bio) == WRITE); /* only write */ + + do { + struct page *page = bvec->bv_page; + + if (--bvec >= bio->bi_io_vec) + prefetchw(&bvec->bv_page->flags); + if (err) { + SetPageError(page); + if (page->mapping) + mapping_set_error(page->mapping, err); + } + + __dfr_writepage_end_io(page); + + end_page_writeback(page); + } while (bvec >= bio->bi_io_vec); + bio_put(bio); +} + +static struct bio *mpage_bio_submit_write(int flags, struct bio *bio) +{ + bio->bi_end_io = mpage_write_end_io; + __sdfat_submit_bio_write2(flags, bio); + return NULL; +} + +static struct bio * +mpage_alloc(struct block_device *bdev, + sector_t first_sector, int nr_vecs, + gfp_t gfp_flags) +{ + struct bio *bio; + + bio = bio_alloc(gfp_flags, nr_vecs); + + if (bio == NULL && (current->flags & PF_MEMALLOC)) { + while (!bio && (nr_vecs /= 2)) + bio = bio_alloc(gfp_flags, nr_vecs); + } + + if (bio) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + bio_set_dev(bio, bdev); +#else + bio->bi_bdev = bdev; +#endif + __sdfat_set_bio_sector(bio, first_sector); + } + return bio; +} + +static int sdfat_mpage_writepage(struct page *page, + struct writeback_control *wbc, void *data) +{ + struct mpage_data *mpd = data; + struct bio *bio = mpd->bio; + struct address_space *mapping = page->mapping; + struct inode *inode = page->mapping->host; + const unsigned int blkbits = inode->i_blkbits; + const unsigned int blocks_per_page = PAGE_SIZE >> blkbits; + sector_t last_block; + sector_t block_in_file; + sector_t blocks[MAX_BUF_PER_PAGE]; + unsigned int page_block; + unsigned int first_unmapped = blocks_per_page; + struct block_device *bdev = NULL; + int boundary = 0; + sector_t boundary_block = 0; + struct block_device *boundary_bdev = NULL; + int length; + struct buffer_head map_bh; + loff_t i_size = i_size_read(inode); + unsigned long end_index = i_size >> PAGE_SHIFT; + int ret = 0; + + if (page_has_buffers(page)) { + struct buffer_head *head = page_buffers(page); + struct buffer_head *bh = head; + + /* If they're all mapped and dirty, do it */ + page_block = 0; + do { + BUG_ON(buffer_locked(bh)); + if (!buffer_mapped(bh)) { + /* + * unmapped dirty buffers are created by + * __set_page_dirty_buffers -> mmapped data + */ + if (buffer_dirty(bh)) + goto confused; + if (first_unmapped == blocks_per_page) + first_unmapped = page_block; + continue; + } + + if (first_unmapped != blocks_per_page) + goto confused; /* hole -> non-hole */ + + if (!buffer_dirty(bh) || !buffer_uptodate(bh)) + goto confused; + + /* bh should be mapped if delay is set */ + if (buffer_delay(bh)) { + sector_t blk_in_file = + (sector_t)(page->index << (PAGE_SHIFT - blkbits)) + page_block; + + BUG_ON(bh->b_size != (1 << blkbits)); + if (page->index > end_index) { + MMSG("%s(inode:%p) " + "over end with delayed buffer" + "(page_idx:%u, end_idx:%u)\n", + __func__, inode, + (u32)page->index, + (u32)end_index); + goto confused; + } + + ret = mpd->get_block(inode, blk_in_file, bh, 1); + if (ret) { + MMSG("%s(inode:%p) " + "failed to getblk(ret:%d)\n", + __func__, inode, ret); + goto confused; + } + + BUG_ON(buffer_delay(bh)); + + if (buffer_new(bh)) { + clear_buffer_new(bh); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + clean_bdev_bh_alias(bh); +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) */ + unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr); +#endif + } + } + + if (page_block) { + if (bh->b_blocknr != blocks[page_block-1] + 1) { + MMSG("%s(inode:%p) pblk(%d) " + "no_seq(prev:%lld, new:%lld)\n", + __func__, inode, page_block, + (u64)blocks[page_block-1], + (u64)bh->b_blocknr); + goto confused; + } + } + blocks[page_block++] = bh->b_blocknr; + boundary = buffer_boundary(bh); + if (boundary) { + boundary_block = bh->b_blocknr; + boundary_bdev = bh->b_bdev; + } + bdev = bh->b_bdev; + } while ((bh = bh->b_this_page) != head); + + if (first_unmapped) + goto page_is_mapped; + + /* + * Page has buffers, but they are all unmapped. The page was + * created by pagein or read over a hole which was handled by + * block_read_full_page(). If this address_space is also + * using mpage_readpages then this can rarely happen. + */ + goto confused; + } + + /* + * The page has no buffers: map it to disk + */ + BUG_ON(!PageUptodate(page)); + block_in_file = (sector_t)page->index << (PAGE_SHIFT - blkbits); + last_block = (i_size - 1) >> blkbits; + map_bh.b_page = page; + for (page_block = 0; page_block < blocks_per_page; ) { + + map_bh.b_state = 0; + map_bh.b_size = 1 << blkbits; + if (mpd->get_block(inode, block_in_file, &map_bh, 1)) + goto confused; + + if (buffer_new(&map_bh)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + clean_bdev_bh_alias(&map_bh); +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) */ + unmap_underlying_metadata(map_bh.b_bdev, + map_bh.b_blocknr); +#endif + if (buffer_boundary(&map_bh)) { + boundary_block = map_bh.b_blocknr; + boundary_bdev = map_bh.b_bdev; + } + + if (page_block) { + if (map_bh.b_blocknr != blocks[page_block-1] + 1) + goto confused; + } + blocks[page_block++] = map_bh.b_blocknr; + boundary = buffer_boundary(&map_bh); + bdev = map_bh.b_bdev; + if (block_in_file == last_block) + break; + block_in_file++; + } + BUG_ON(page_block == 0); + + first_unmapped = page_block; + +page_is_mapped: + if (page->index >= end_index) { + /* + * The page straddles i_size. It must be zeroed out on each + * and every writepage invocation because it may be mmapped. + * "A file is mapped in multiples of the page size. For a file + * that is not a multiple of the page size, the remaining memory + * is zeroed when mapped, and writes to that region are not + * written out to the file." + */ + unsigned int offset = i_size & (PAGE_SIZE - 1); + + if (page->index > end_index || !offset) { + MMSG("%s(inode:%p) over end " + "(page_idx:%u, end_idx:%u off:%u)\n", + __func__, inode, (u32)page->index, + (u32)end_index, (u32)offset); + goto confused; + } + zero_user_segment(page, offset, PAGE_SIZE); + } + + /* + * This page will go to BIO. Do we need to send this BIO off first? + * + * REMARK : added ELSE_IF for ALIGNMENT_MPAGE_WRITE of SDFAT + */ + if (bio) { + if (mpd->last_block_in_bio != blocks[0] - 1) { + bio = mpage_bio_submit_write(0, bio); + } else if (mpd->size_to_align) { + unsigned int mask = mpd->size_to_align - 1; + sector_t max_end_block = + (__sdfat_bio_sector(bio) & ~(mask)) + mask; + + if ((__sdfat_bio_size(bio) != (1 << (mask + 1))) && + (mpd->last_block_in_bio == max_end_block)) { + MMSG("%s(inode:%p) alignment mpage_bio_submit" + "(start:%u, len:%u aligned:%u)\n", + __func__, inode, + (unsigned int)__sdfat_bio_sector(bio), + (unsigned int)(mpd->last_block_in_bio - + __sdfat_bio_sector(bio) + 1), + (unsigned int)mpd->size_to_align); + bio = mpage_bio_submit_write(REQ_NOMERGE, bio); + } + } + } + +alloc_new: + if (!bio) { + bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9), + bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH); + if (!bio) + goto confused; + } + + /* + * Must try to add the page before marking the buffer clean or + * the confused fail path above (OOM) will be very confused when + * it finds all bh marked clean (i.e. it will not write anything) + */ + length = first_unmapped << blkbits; + if (bio_add_page(bio, page, length, 0) < length) { + bio = mpage_bio_submit_write(0, bio); + goto alloc_new; + } + + /* + * OK, we have our BIO, so we can now mark the buffers clean. Make + * sure to only clean buffers which we know we'll be writing. + */ + if (page_has_buffers(page)) { + struct buffer_head *head = page_buffers(page); + struct buffer_head *bh = head; + unsigned int buffer_counter = 0; + + do { + if (buffer_counter++ == first_unmapped) + break; + clear_buffer_dirty(bh); + bh = bh->b_this_page; + } while (bh != head); + + /* + * we cannot drop the bh if the page is not uptodate + * or a concurrent readpage would fail to serialize with the bh + * and it would read from disk before we reach the platter. + */ + if (buffer_heads_over_limit && PageUptodate(page)) + try_to_free_buffers(page); + } + + BUG_ON(PageWriteback(page)); + set_page_writeback(page); + + /* + * FIXME FOR DEFRAGMENTATION : CODE REVIEW IS REQUIRED + * + * Turn off MAPPED flag in victim's bh if defrag on. + * Another write_begin can starts after get_block for defrag victims + * called. + * In this case, write_begin calls get_block and get original block + * number and previous defrag will be canceled. + */ + if (unlikely(__check_dfr_on(inode, (loff_t)(page->index << PAGE_SHIFT), + (loff_t)((page->index + 1) << PAGE_SHIFT), __func__))) { + struct buffer_head *head = page_buffers(page); + struct buffer_head *bh = head; + + do { + clear_buffer_mapped(bh); + bh = bh->b_this_page; + } while (bh != head); + } + + unlock_page(page); + if (boundary || (first_unmapped != blocks_per_page)) { + bio = mpage_bio_submit_write(0, bio); + if (boundary_block) { + write_boundary_block(boundary_bdev, + boundary_block, 1 << blkbits); + } + } else { + mpd->last_block_in_bio = blocks[blocks_per_page - 1]; + } + + goto out; + +confused: + if (bio) + bio = mpage_bio_submit_write(0, bio); + + if (mpd->use_writepage) { + ret = mapping->a_ops->writepage(page, wbc); + } else { + ret = -EAGAIN; + goto out; + } + /* + * The caller has a ref on the inode, so *mapping is stable + */ + mapping_set_error(mapping, ret); +out: + mpd->bio = bio; + return ret; +} + +int sdfat_mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t *get_block) +{ + struct blk_plug plug; + int ret; + struct mpage_data mpd = { + .bio = NULL, + .last_block_in_bio = 0, + .get_block = get_block, + .use_writepage = 1, + .size_to_align = __calc_size_to_align(mapping->host->i_sb), + }; + + BUG_ON(!get_block); + blk_start_plug(&plug); + ret = write_cache_pages(mapping, wbc, sdfat_mpage_writepage, &mpd); + if (mpd.bio) + mpage_bio_submit_write(0, mpd.bio); + blk_finish_plug(&plug); + return ret; +} + +#endif /* CONFIG_SDFAT_ALIGNED_MPAGE_WRITE */ diff --git a/fs/sdfat/nls.c b/fs/sdfat/nls.c new file mode 100644 index 0000000000000..b65634454c557 --- /dev/null +++ b/fs/sdfat/nls.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : nls.c */ +/* PURPOSE : sdFAT NLS Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ +#include +#include + +#include "sdfat.h" +#include "core.h" + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static u16 bad_dos_chars[] = { + /* + , ; = [ ] */ + 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, + 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, + 0 +}; + +/* + * Allow full-width illegal characters : + * "MS windows 7" supports full-width-invalid-name-characters. + * So we should check half-width-invalid-name-characters(ASCII) only + * for compatibility. + * + * " * / : < > ? \ | + * + * patch 1.2.0 + */ +static u16 bad_uni_chars[] = { + 0x0022, 0x002A, 0x002F, 0x003A, + 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, +#if 0 /* allow full-width characters */ + 0x201C, 0x201D, 0xFF0A, 0xFF0F, 0xFF1A, + 0xFF1C, 0xFF1E, 0xFF1F, 0xFF3C, 0xFF5C, +#endif + 0 +}; + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ +static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy); +static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy); + +static u16 nls_upper(struct super_block *sb, u16 a) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + if (SDFAT_SB(sb)->options.casesensitive) + return a; + if ((fsi->vol_utbl)[get_col_index(a)] != NULL) + return (fsi->vol_utbl)[get_col_index(a)][get_row_index(a)]; + else + return a; +} +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ +u16 *nls_wstrchr(u16 *str, u16 wchar) +{ + while (*str) { + if (*(str++) == wchar) + return str; + } + + return 0; +} + +s32 nls_cmp_sfn(struct super_block *sb, u8 *a, u8 *b) +{ + return strncmp((void *)a, (void *)b, DOS_NAME_LENGTH); +} + +s32 nls_cmp_uniname(struct super_block *sb, u16 *a, u16 *b) +{ + s32 i; + + for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { + if (nls_upper(sb, *a) != nls_upper(sb, *b)) + return 1; + if (*a == 0x0) + return 0; + } + return 0; +} + +#define CASE_LOWER_BASE (0x08) /* base is lower case */ +#define CASE_LOWER_EXT (0x10) /* extension is lower case */ + +s32 nls_uni16s_to_sfn(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname, s32 *p_lossy) +{ + s32 i, j, len, lossy = NLS_NAME_NO_LOSSY; + u8 buf[MAX_CHARSET_SIZE]; + u8 lower = 0, upper = 0; + u8 *dosname = p_dosname->name; + u16 *uniname = p_uniname->name; + u16 *p, *last_period; + struct nls_table *nls = SDFAT_SB(sb)->nls_disk; + + /* DOSNAME is filled with space */ + for (i = 0; i < DOS_NAME_LENGTH; i++) + *(dosname+i) = ' '; + + /* DOT and DOTDOT are handled by VFS layer */ + + /* search for the last embedded period */ + last_period = NULL; + for (p = uniname; *p; p++) { + if (*p == (u16) '.') + last_period = p; + } + + i = 0; + while (i < DOS_NAME_LENGTH) { + if (i == 8) { + if (last_period == NULL) + break; + + if (uniname <= last_period) { + if (uniname < last_period) + lossy |= NLS_NAME_OVERLEN; + uniname = last_period + 1; + } + } + + if (*uniname == (u16) '\0') { + break; + } else if (*uniname == (u16) ' ') { + lossy |= NLS_NAME_LOSSY; + } else if (*uniname == (u16) '.') { + if (uniname < last_period) + lossy |= NLS_NAME_LOSSY; + else + i = 8; + } else if (nls_wstrchr(bad_dos_chars, *uniname)) { + lossy |= NLS_NAME_LOSSY; + *(dosname+i) = '_'; + i++; + } else { + len = convert_uni_to_ch(nls, *uniname, buf, &lossy); + + if (len > 1) { + if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) + break; + + if ((i < 8) && ((i+len) > 8)) { + i = 8; + continue; + } + + lower = 0xFF; + + for (j = 0; j < len; j++, i++) + *(dosname+i) = *(buf+j); + } else { /* len == 1 */ + if ((*buf >= 'a') && (*buf <= 'z')) { + *(dosname+i) = *buf - ('a' - 'A'); + + lower |= (i < 8) ? + CASE_LOWER_BASE : + CASE_LOWER_EXT; + } else if ((*buf >= 'A') && (*buf <= 'Z')) { + *(dosname+i) = *buf; + + upper |= (i < 8) ? + CASE_LOWER_BASE : + CASE_LOWER_EXT; + } else { + *(dosname+i) = *buf; + } + i++; + } + } + + uniname++; + } + + if (*dosname == 0xE5) + *dosname = 0x05; + if (*uniname != 0x0) + lossy |= NLS_NAME_OVERLEN; + + if (upper & lower) + p_dosname->name_case = 0xFF; + else + p_dosname->name_case = lower; + + if (p_lossy) + *p_lossy = lossy; + return i; +} + +s32 nls_sfn_to_uni16s(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname) +{ + s32 i = 0, j, n = 0; + u8 buf[MAX_DOSNAME_BUF_SIZE]; + u8 *dosname = p_dosname->name; + u16 *uniname = p_uniname->name; + struct nls_table *nls = SDFAT_SB(sb)->nls_disk; + + if (*dosname == 0x05) { + *buf = 0xE5; + i++; + n++; + } + + for ( ; i < 8; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && + (p_dosname->name_case & CASE_LOWER_BASE)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + if (*(dosname+8) != ' ') { + *(buf+n) = '.'; + n++; + } + + for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && + (p_dosname->name_case & CASE_LOWER_EXT)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + *(buf+n) = '\0'; + + i = j = 0; + while (j < MAX_NAME_LENGTH) { + if (*(buf+i) == '\0') + break; + + i += convert_ch_to_uni(nls, (buf+i), uniname, NULL); + + uniname++; + j++; + } + + *uniname = (u16) '\0'; + return j; +} + +static s32 __nls_utf16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen) +{ + s32 len; + const u16 *uniname = p_uniname->name; + + /* always len >= 0 */ + len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, + p_cstring, buflen); + p_cstring[len] = '\0'; + return len; +} + +static s32 __nls_vfsname_to_utf16s(struct super_block *sb, const u8 *p_cstring, + const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy) +{ + s32 i, unilen, lossy = NLS_NAME_NO_LOSSY; + u16 upname[MAX_NAME_LENGTH+1]; + u16 *uniname = p_uniname->name; + + BUG_ON(!len); + + unilen = utf8s_to_utf16s(p_cstring, len, UTF16_HOST_ENDIAN, + (wchar_t *)uniname, MAX_NAME_LENGTH+2); + if (unilen < 0) { + MMSG("%s: failed to vfsname_to_utf16(err:%d) " + "vfsnamelen:%d", __func__, unilen, len); + return unilen; + } + + if (unilen > MAX_NAME_LENGTH) { + MMSG("%s: failed to vfsname_to_utf16(estr:ENAMETOOLONG) " + "vfsnamelen:%d, unilen:%d>%d", + __func__, len, unilen, MAX_NAME_LENGTH); + return -ENAMETOOLONG; + } + + p_uniname->name_len = (u8)(unilen & 0xFF); + + for (i = 0; i < unilen; i++) { + if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname)) + lossy |= NLS_NAME_LOSSY; + + *(upname+i) = nls_upper(sb, *uniname); + uniname++; + } + + *uniname = (u16)'\0'; + p_uniname->name_len = unilen; + p_uniname->name_hash = calc_chksum_2byte((void *) upname, + unilen << 1, 0, CS_DEFAULT); + + if (p_lossy) + *p_lossy = lossy; + + return unilen; +} + +static s32 __nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen) +{ + s32 i, j, len, out_len = 0; + u8 buf[MAX_CHARSET_SIZE]; + const u16 *uniname = p_uniname->name; + struct nls_table *nls = SDFAT_SB(sb)->nls_io; + + i = 0; + while ((i < MAX_NAME_LENGTH) && (out_len < (buflen-1))) { + if (*uniname == (u16)'\0') + break; + + len = convert_uni_to_ch(nls, *uniname, buf, NULL); + + if (out_len + len >= buflen) + len = (buflen - 1) - out_len; + + out_len += len; + + if (len > 1) { + for (j = 0; j < len; j++) + *p_cstring++ = (s8) *(buf+j); + } else { /* len == 1 */ + *p_cstring++ = (s8) *buf; + } + + uniname++; + i++; + } + + *p_cstring = '\0'; + return out_len; +} + +static s32 __nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, + const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy) +{ + s32 i, unilen, lossy = NLS_NAME_NO_LOSSY; + u16 upname[MAX_NAME_LENGTH+1]; + u16 *uniname = p_uniname->name; + struct nls_table *nls = SDFAT_SB(sb)->nls_io; + + BUG_ON(!len); + + i = unilen = 0; + while ((unilen < MAX_NAME_LENGTH) && (i < len)) { + i += convert_ch_to_uni(nls, (u8 *)(p_cstring+i), uniname, &lossy); + + if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname)) + lossy |= NLS_NAME_LOSSY; + + *(upname+unilen) = nls_upper(sb, *uniname); + + uniname++; + unilen++; + } + + if (*(p_cstring+i) != '\0') + lossy |= NLS_NAME_OVERLEN; + + *uniname = (u16)'\0'; + p_uniname->name_len = unilen; + p_uniname->name_hash = + calc_chksum_2byte((void *) upname, unilen<<1, 0, CS_DEFAULT); + + if (p_lossy) + *p_lossy = lossy; + + return unilen; +} + +s32 nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *uniname, u8 *p_cstring, s32 buflen) +{ + if (SDFAT_SB(sb)->options.utf8) + return __nls_utf16s_to_vfsname(sb, uniname, p_cstring, buflen); + + return __nls_uni16s_to_vfsname(sb, uniname, p_cstring, buflen); +} + +s32 nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, const s32 len, UNI_NAME_T *uniname, s32 *p_lossy) +{ + if (SDFAT_SB(sb)->options.utf8) + return __nls_vfsname_to_utf16s(sb, p_cstring, len, uniname, p_lossy); + return __nls_vfsname_to_uni16s(sb, p_cstring, len, uniname, p_lossy); +} + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy) +{ + int len; + + *uni = 0x0; + + if (ch[0] < 0x80) { + *uni = (u16) ch[0]; + return 1; + } + + len = nls->char2uni(ch, MAX_CHARSET_SIZE, uni); + if (len < 0) { + /* conversion failed */ + DMSG("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy |= NLS_NAME_LOSSY; + *uni = (u16) '_'; + if (!strcmp(nls->charset, "utf8")) + return 1; + return 2; + } + + return len; +} /* end of convert_ch_to_uni */ + +static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy) +{ + int len; + + ch[0] = 0x0; + + if (uni < 0x0080) { + ch[0] = (u8) uni; + return 1; + } + + len = nls->uni2char(uni, ch, MAX_CHARSET_SIZE); + if (len < 0) { + /* conversion failed */ + DMSG("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy |= NLS_NAME_LOSSY; + ch[0] = '_'; + return 1; + } + + return len; + +} /* end of convert_uni_to_ch */ + +/* end of nls.c */ diff --git a/fs/sdfat/sdfat.c b/fs/sdfat/sdfat.c new file mode 100644 index 0000000000000..d83bf369b4dee --- /dev/null +++ b/fs/sdfat/sdfat.c @@ -0,0 +1,5133 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : core.c */ +/* PURPOSE : sdFAT glue layer for supporting VFS */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mark_page_accessed() */ +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) +#error SDFAT only supports linux kernel version 3.0 or higher +#endif + +#include "sdfat.h" +#include "version.h" + +/* skip iterating emit_dots when dir is empty */ +#define ITER_POS_FILLED_DOTS (2) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) +#define current_time(x) (CURRENT_TIME_SEC) +#endif +/* type index declare at sdfat.h */ +const char *FS_TYPE_STR[] = { + "auto", + "exfat", + "vfat" +}; + +static struct kset *sdfat_kset; +static struct kmem_cache *sdfat_inode_cachep; + + +static int sdfat_default_codepage = CONFIG_SDFAT_DEFAULT_CODEPAGE; +static char sdfat_default_iocharset[] = CONFIG_SDFAT_DEFAULT_IOCHARSET; +static const char sdfat_iocharset_with_utf8[] = "iso8859-1"; + +#ifdef CONFIG_SDFAT_TRACE_SB_LOCK +static unsigned long __lock_jiffies; +#endif + +static void sdfat_truncate(struct inode *inode, loff_t old_size); +static int sdfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create); + +static struct inode *sdfat_iget(struct super_block *sb, loff_t i_pos); +static struct inode *sdfat_build_inode(struct super_block *sb, const FILE_ID_T *fid, loff_t i_pos); +static void sdfat_detach(struct inode *inode); +static void sdfat_attach(struct inode *inode, loff_t i_pos); +static inline unsigned long sdfat_hash(loff_t i_pos); +static int __sdfat_write_inode(struct inode *inode, int sync); +static int sdfat_sync_inode(struct inode *inode); +static int sdfat_write_inode(struct inode *inode, struct writeback_control *wbc); +static void sdfat_write_super(struct super_block *sb); +static void sdfat_write_failed(struct address_space *mapping, loff_t to); + +static void sdfat_init_namebuf(DENTRY_NAMEBUF_T *nb); +static int sdfat_alloc_namebuf(DENTRY_NAMEBUF_T *nb); +static void sdfat_free_namebuf(DENTRY_NAMEBUF_T *nb); + +/************************************************************************* + * INNER FUNCTIONS FOR FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +static void __sdfat_writepage_end_io(struct bio *bio, int err); +static inline void __lock_super(struct super_block *sb); +static inline void __unlock_super(struct super_block *sb); +static int __sdfat_create(struct inode *dir, struct dentry *dentry); +static int __sdfat_revalidate(struct dentry *dentry); +static int __sdfat_revalidate_ci(struct dentry *dentry, unsigned int flags); +static int __sdfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync); +static struct dentry *__sdfat_lookup(struct inode *dir, struct dentry *dentry); +static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry); +static int __sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +static int __sdfat_show_options(struct seq_file *m, struct super_block *sb); +static inline ssize_t __sdfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs); +static inline ssize_t __sdfat_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + loff_t count, unsigned long nr_segs); +static int __sdfat_d_hash(const struct dentry *dentry, struct qstr *qstr); +static int __sdfat_d_hashi(const struct dentry *dentry, struct qstr *qstr); +static int __sdfat_cmp(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name); +static int __sdfat_cmpi(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name); + +/************************************************************************* + * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) +static int sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + /* + * The VFS already checks for existence, so for local filesystems + * the RENAME_NOREPLACE implementation is equivalent to plain rename. + * Don't support any other flags + */ + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + return __sdfat_rename(old_dir, old_dentry, new_dir, new_dentry); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */ +static int sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + return __sdfat_rename(old_dir, old_dentry, new_dir, new_dentry); +} + +static int setattr_prepare(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + + return inode_change_ok(inode, attr); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) +static inline void __sdfat_submit_bio_write(struct bio *bio) +{ + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); + submit_bio(bio); +} + +static inline unsigned int __sdfat_full_name_hash(const struct dentry *dentry, const char *name, unsigned int len) +{ + return full_name_hash(dentry, name, len); +} + +static inline unsigned long __sdfat_init_name_hash(const struct dentry *dentry) +{ + return init_name_hash(dentry); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) */ +static inline void __sdfat_submit_bio_write(struct bio *bio) +{ + submit_bio(WRITE, bio); +} + +static inline unsigned int __sdfat_full_name_hash(const struct dentry *unused, const char *name, unsigned int len) +{ + return full_name_hash(name, len); +} + +static inline unsigned long __sdfat_init_name_hash(const struct dentry *unused) +{ + return init_name_hash(); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 21) + /* EMPTY */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 21) */ +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); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) +static void sdfat_writepage_end_io(struct bio *bio) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + __sdfat_writepage_end_io(bio, bio->bi_status); +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) */ + __sdfat_writepage_end_io(bio, bio->bi_error); +#endif +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) */ +static void sdfat_writepage_end_io(struct bio *bio, int err) +{ + if (test_bit(BIO_UPTODATE, &bio->bi_flags)) + err = 0; + __sdfat_writepage_end_io(bio, err); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static inline int sdfat_remount_syncfs(struct super_block *sb) +{ + sync_filesystem(sb); + return 0; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) */ +static inline int sdfat_remount_syncfs(struct super_block *sb) +{ + /* + * We don`t need to call sync_filesystem(sb), + * Because VFS calls it. + */ + return 0; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static inline sector_t __sdfat_bio_sector(struct bio *bio) +{ + return bio->bi_iter.bi_sector; +} + +static inline void __sdfat_set_bio_iterate(struct bio *bio, sector_t sector, + unsigned int size, unsigned int idx, unsigned int done) +{ + struct bvec_iter *iter = &(bio->bi_iter); + + iter->bi_sector = sector; + iter->bi_size = size; + iter->bi_idx = idx; + iter->bi_bvec_done = done; +} + +static void __sdfat_truncate_pagecache(struct inode *inode, + loff_t to, loff_t newsize) +{ + truncate_pagecache(inode, newsize); +} + +static int sdfat_d_hash(const struct dentry *dentry, struct qstr *qstr) +{ + return __sdfat_d_hash(dentry, qstr); +} + +static int sdfat_d_hashi(const struct dentry *dentry, struct qstr *qstr) +{ + return __sdfat_d_hashi(dentry, qstr); +} + +//instead of sdfat_readdir +static int sdfat_iterate(struct file *filp, struct dir_context *ctx) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + DIR_ENTRY_T de; + DENTRY_NAMEBUF_T *nb = &(de.NameBuf); + unsigned long inum; + loff_t cpos; + int err = 0, fake_offset = 0; + + sdfat_init_namebuf(nb); + __lock_super(sb); + + cpos = ctx->pos; + if ((fsi->vol_type == EXFAT) || (inode->i_ino == SDFAT_ROOT_INO)) { + if (!dir_emit_dots(filp, ctx)) + goto out; + if (ctx->pos == ITER_POS_FILLED_DOTS) { + cpos = 0; + fake_offset = 1; + } + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + + /* name buffer should be allocated before use */ + err = sdfat_alloc_namebuf(nb); + if (err) + goto out; +get_new: + SDFAT_I(inode)->fid.size = i_size_read(inode); + SDFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + if (cpos >= SDFAT_I(inode)->fid.size) + goto end_of_dir; + + err = fsapi_readdir(inode, &de); + if (err) { + // at least we tried to read a sector + // move cpos to next sector position (should be aligned) + if (err == -EIO) { + cpos += 1 << (sb->s_blocksize_bits); + cpos &= ~((u32)sb->s_blocksize-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = SDFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!nb->lfn[0]) + goto end_of_dir; + + if (!memcmp(nb->sfn, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(nb->sfn, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) SDFAT_I(inode)->fid.start_clu << 32) | + ((SDFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + struct inode *tmp = sdfat_iget(sb, i_pos); + + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, SDFAT_ROOT_INO); + } + } + + /* Before calling dir_emit(), sb_lock should be released. + * Because page fault can occur in dir_emit() when the size of buffer given + * from user is larger than one page size + */ + __unlock_super(sb); + if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) + goto out_unlocked; + __lock_super(sb); + + ctx->pos = cpos; + goto get_new; + +end_of_dir: + if (!cpos && fake_offset) + cpos = ITER_POS_FILLED_DOTS; + ctx->pos = cpos; +out: + __unlock_super(sb); +out_unlocked: + /* + * To improve performance, free namebuf after unlock sb_lock. + * If namebuf is not allocated, this function do nothing + */ + sdfat_free_namebuf(nb); + return err; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */ +static inline sector_t __sdfat_bio_sector(struct bio *bio) +{ + return bio->bi_sector; +} + +static inline void __sdfat_set_bio_iterate(struct bio *bio, sector_t sector, + unsigned int size, unsigned int idx, unsigned int done) +{ + bio->bi_sector = sector; + bio->bi_idx = idx; + bio->bi_size = size; //PAGE_SIZE; +} + +static void __sdfat_truncate_pagecache(struct inode *inode, + loff_t to, loff_t newsize) +{ + truncate_pagecache(inode, to, newsize); +} + +static int sdfat_d_hash(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + return __sdfat_d_hash(dentry, qstr); +} + +static int sdfat_d_hashi(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + return __sdfat_d_hashi(dentry, qstr); +} + +static int sdfat_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + DIR_ENTRY_T de; + DENTRY_NAMEBUF_T *nb = &(de.NameBuf); + unsigned long inum; + loff_t cpos; + int err = 0, fake_offset = 0; + + sdfat_init_namebuf(nb); + __lock_super(sb); + + cpos = filp->f_pos; + /* Fake . and .. for the root directory. */ + if ((fsi->vol_type == EXFAT) || (inode->i_ino == SDFAT_ROOT_INO)) { + while (cpos < ITER_POS_FILLED_DOTS) { + if (inode->i_ino == SDFAT_ROOT_INO) + inum = SDFAT_ROOT_INO; + else if (cpos == 0) + inum = inode->i_ino; + else /* (cpos == 1) */ + inum = parent_ino(filp->f_path.dentry); + + if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0) + goto out; + cpos++; + filp->f_pos++; + } + if (cpos == ITER_POS_FILLED_DOTS) { + cpos = 0; + fake_offset = 1; + } + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + + /* name buffer should be allocated before use */ + err = sdfat_alloc_namebuf(nb); + if (err) + goto out; +get_new: + SDFAT_I(inode)->fid.size = i_size_read(inode); + SDFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + if (cpos >= SDFAT_I(inode)->fid.size) + goto end_of_dir; + + err = fsapi_readdir(inode, &de); + if (err) { + // at least we tried to read a sector + // move cpos to next sector position (should be aligned) + if (err == -EIO) { + cpos += 1 << (sb->s_blocksize_bits); + cpos &= ~((u32)sb->s_blocksize-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = SDFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!nb->lfn[0]) + goto end_of_dir; + + if (!memcmp(nb->sfn, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(nb->sfn, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) SDFAT_I(inode)->fid.start_clu << 32) | + ((SDFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + struct inode *tmp = sdfat_iget(sb, i_pos); + + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, SDFAT_ROOT_INO); + } + } + + /* Before calling dir_emit(), sb_lock should be released. + * Because page fault can occur in dir_emit() when the size of buffer given + * from user is larger than one page size + */ + __unlock_super(sb); + if (filldir(dirent, nb->lfn, strlen(nb->lfn), cpos, inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0) + goto out_unlocked; + __lock_super(sb); + + filp->f_pos = cpos; + goto get_new; + +end_of_dir: + if (!cpos && fake_offset) + cpos = ITER_POS_FILLED_DOTS; + filp->f_pos = cpos; +out: + __unlock_super(sb); +out_unlocked: + /* + * To improve performance, free namebuf after unlock sb_lock. + * If namebuf is not allocated, this function do nothing + */ + sdfat_free_namebuf(nb); + return err; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + /* EMPTY */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) */ +static inline struct inode *file_inode(const struct file *f) +{ + return f->f_dentry->d_inode; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) +static inline int __is_sb_dirty(struct super_block *sb) +{ + return SDFAT_SB(sb)->s_dirt; +} + +static inline void __set_sb_clean(struct super_block *sb) +{ + SDFAT_SB(sb)->s_dirt = 0; +} + +/* Workqueue wrapper for sdfat_write_super () */ +static void __write_super_delayed(struct work_struct *work) +{ + struct sdfat_sb_info *sbi; + struct super_block *sb; + + sbi = container_of(work, struct sdfat_sb_info, write_super_work.work); + sb = sbi->host_sb; + + /* XXX: Is this needed? */ + if (!sb || !down_read_trylock(&sb->s_umount)) { + DMSG("%s: skip delayed work(write_super).\n", __func__); + return; + } + + DMSG("%s: do delayed_work(write_super).\n", __func__); + + spin_lock(&sbi->work_lock); + sbi->write_super_queued = 0; + spin_unlock(&sbi->work_lock); + + sdfat_write_super(sb); + + up_read(&sb->s_umount); +} + +static void setup_sdfat_sync_super_wq(struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + mutex_init(&sbi->s_lock); + spin_lock_init(&sbi->work_lock); + INIT_DELAYED_WORK(&sbi->write_super_work, __write_super_delayed); + sbi->host_sb = sb; +} + +static inline bool __cancel_delayed_work_sync(struct sdfat_sb_info *sbi) +{ + return cancel_delayed_work_sync(&sbi->write_super_work); +} + +static inline void lock_super(struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + mutex_lock(&sbi->s_lock); +} + +static inline void unlock_super(struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + mutex_unlock(&sbi->s_lock); +} + +static int sdfat_revalidate(struct dentry *dentry, unsigned int flags) +{ + if (flags & LOOKUP_RCU) + return -ECHILD; + + return __sdfat_revalidate(dentry); +} + +static int sdfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +{ + if (flags & LOOKUP_RCU) + return -ECHILD; + + return __sdfat_revalidate_ci(dentry, flags); +} + +static struct inode *sdfat_iget(struct super_block *sb, loff_t i_pos) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct sdfat_inode_info *info; + struct hlist_head *head = sbi->inode_hashtable + sdfat_hash(i_pos); + struct inode *inode = NULL; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, head, i_hash_fat) { + BUG_ON(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) */ +static inline int __is_sb_dirty(struct super_block *sb) +{ + return sb->s_dirt; +} + +static inline void __set_sb_clean(struct super_block *sb) +{ + sb->s_dirt = 0; +} + +static void setup_sdfat_sync_super_wq(struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + sbi->host_sb = sb; +} + +static inline bool __cancel_delayed_work_sync(struct sdfat_sb_info *sbi) +{ + /* DO NOTHING */ + return 0; +} + +static inline void clear_inode(struct inode *inode) +{ + end_writeback(inode); +} + +static int sdfat_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + return __sdfat_revalidate(dentry); +} + +static int sdfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) +{ + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + return __sdfat_revalidate_ci(dentry, nd ? nd->flags : 0); + +} + +static struct inode *sdfat_iget(struct super_block *sb, loff_t i_pos) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct sdfat_inode_info *info; + struct hlist_node *node; + struct hlist_head *head = sbi->inode_hashtable + sdfat_hash(i_pos); + struct inode *inode = NULL; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, node, head, i_hash_fat) { + BUG_ON(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +static struct dentry *sdfat_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + return __sdfat_lookup(dir, dentry); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) */ +static struct dentry *sdfat_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + return __sdfat_lookup(dir, dentry); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + /* NOTHING NOW */ +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */ +#define GLOBAL_ROOT_UID (0) +#define GLOBAL_ROOT_GID (0) + +static inline bool uid_eq(uid_t left, uid_t right) +{ + return left == right; +} + +static inline bool gid_eq(gid_t left, gid_t right) +{ + return left == right; +} + +static inline uid_t from_kuid_munged(struct user_namespace *to, uid_t kuid) +{ + return kuid; +} + +static inline gid_t from_kgid_munged(struct user_namespace *to, gid_t kgid) +{ + return kgid; +} + +static inline uid_t make_kuid(struct user_namespace *from, uid_t uid) +{ + return uid; +} + +static inline gid_t make_kgid(struct user_namespace *from, gid_t gid) +{ + return gid; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static struct dentry *__d_make_root(struct inode *root_inode) +{ + return d_make_root(root_inode); +} + +static void __sdfat_do_truncate(struct inode *inode, loff_t old, loff_t new) +{ + down_write(&SDFAT_I(inode)->truncate_lock); + truncate_setsize(inode, new); + sdfat_truncate(inode, old); + up_write(&SDFAT_I(inode)->truncate_lock); +} + +static sector_t sdfat_aop_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* sdfat_get_cluster() assumes the requested blocknr isn't truncated. */ + down_read(&SDFAT_I(mapping->host)->truncate_lock); + blocknr = generic_block_bmap(mapping, block, sdfat_get_block); + up_read(&SDFAT_I(mapping->host)->truncate_lock); + return blocknr; +} + +static int sdfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + return __sdfat_mkdir(dir, dentry); +} + +static int sdfat_show_options(struct seq_file *m, struct dentry *root) +{ + return __sdfat_show_options(m, root->d_sb); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ +static inline void set_nlink(struct inode *inode, unsigned int nlink) +{ + inode->i_nlink = nlink; +} + +static struct dentry *__d_make_root(struct inode *root_inode) +{ + return d_alloc_root(root_inode); +} + +static void __sdfat_do_truncate(struct inode *inode, loff_t old, loff_t new) +{ + truncate_setsize(inode, new); + sdfat_truncate(inode, old); +} + +static sector_t sdfat_aop_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* sdfat_get_cluster() assumes the requested blocknr isn't truncated. */ + down_read(&mapping->host->i_alloc_sem); + blocknr = generic_block_bmap(mapping, block, sdfat_get_block); + up_read(&mapping->host->i_alloc_sem); + return blocknr; +} + +static int sdfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + return __sdfat_mkdir(dir, dentry); +} + +static int sdfat_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + return __sdfat_show_options(m, mnt->mnt_sb); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +#define __sdfat_generic_file_fsync(filp, start, end, datasync) \ + generic_file_fsync(filp, start, end, datasync) + +static int sdfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) +{ + return __sdfat_file_fsync(filp, start, end, datasync); +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) */ +#define __sdfat_generic_file_fsync(filp, start, end, datasync) \ + generic_file_fsync(filp, datasync) +static int sdfat_file_fsync(struct file *filp, int datasync) +{ + return __sdfat_file_fsync(filp, 0, 0, datasync); +} +#endif + +/************************************************************************* + * MORE FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) +static int sdfat_cmp(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __sdfat_cmp(dentry, len, str, name); +} + +static int sdfat_cmpi(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __sdfat_cmpi(dentry, len, str, name); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static int sdfat_cmp(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __sdfat_cmp(dentry, len, str, name); +} + +static int sdfat_cmpi(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + return __sdfat_cmpi(dentry, len, str, name); +} +#else +static int sdfat_cmp(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + return __sdfat_cmp(dentry, len, str, name); +} + +static int sdfat_cmpi(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + return __sdfat_cmpi(dentry, len, str, name); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +static const char *sdfat_follow_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) +{ + struct sdfat_inode_info *ei = SDFAT_I(inode); + + return (char *)(ei->target); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) +static const char *sdfat_follow_link(struct dentry *dentry, void **cookie) +{ + struct sdfat_inode_info *ei = SDFAT_I(dentry->d_inode); + + return *cookie = (char *)(ei->target); +} +#else +static void *sdfat_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct sdfat_inode_info *ei = SDFAT_I(dentry->d_inode); + + nd_set_link(nd, (char *)(ei->target)); + return NULL; +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +static ssize_t sdfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_iter_count(iter); + int rw = iov_iter_rw(iter); + loff_t offset = iocb->ki_pos; + + return __sdfat_direct_IO(rw, iocb, inode, + (void *)iter, offset, count, 0 /* UNUSED */); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +static ssize_t sdfat_direct_IO(struct kiocb *iocb, + struct iov_iter *iter, + loff_t offset) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_iter_count(iter); + int rw = iov_iter_rw(iter); + + return __sdfat_direct_IO(rw, iocb, inode, + (void *)iter, offset, count, 0 /* UNUSED */); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static ssize_t sdfat_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 inode *inode = mapping->host; + size_t count = iov_iter_count(iter); + + return __sdfat_direct_IO(rw, iocb, inode, + (void *)iter, offset, count, 0 /* UNUSED */); +} +#else +static ssize_t sdfat_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, loff_t offset, unsigned long nr_segs) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_length(iov, nr_segs); + + return __sdfat_direct_IO(rw, iocb, inode, + (void *)iov, offset, count, nr_segs); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +static inline ssize_t __sdfat_blkdev_direct_IO(int unused, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t unused_1, + unsigned long nr_segs) +{ + struct iov_iter *iter = (struct iov_iter *)iov_u; + + return blockdev_direct_IO(iocb, inode, iter, sdfat_get_block); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +static inline ssize_t __sdfat_blkdev_direct_IO(int unused, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + struct iov_iter *iter = (struct iov_iter *)iov_u; + + return blockdev_direct_IO(iocb, inode, iter, offset, sdfat_get_block); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static inline ssize_t __sdfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + struct iov_iter *iter = (struct iov_iter *)iov_u; + + return blockdev_direct_IO(rw, iocb, inode, iter, + offset, sdfat_get_block); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static inline ssize_t __sdfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + const struct iovec *iov = (const struct iovec *)iov_u; + + return blockdev_direct_IO(rw, iocb, inode, iov, + offset, nr_segs, sdfat_get_block); +} +#else +static inline ssize_t __sdfat_blkdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + unsigned long nr_segs) +{ + const struct iovec *iov = (const struct iovec *)iov_u; + + return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, sdfat_get_block, NULL); +} +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +static int sdfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +{ + return __sdfat_create(dir, dentry); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +static int sdfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *nd) +{ + return __sdfat_create(dir, dentry); +} +#else +static int sdfat_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + return __sdfat_create(dir, dentry); +} +#endif + + +/************************************************************************* + * WRAP FUNCTIONS FOR DEBUGGING + *************************************************************************/ +#ifdef CONFIG_SDFAT_TRACE_SB_LOCK +static inline void __lock_super(struct super_block *sb) +{ + lock_super(sb); + __lock_jiffies = jiffies; +} + +static inline void __unlock_super(struct super_block *sb) +{ + int time = ((jiffies - __lock_jiffies) * 1000 / HZ); + /* FIXME : error message should be modified */ + if (time > 10) + EMSG("lock_super in %s (%d ms)\n", __func__, time); + + unlock_super(sb); +} +#else /* CONFIG_SDFAT_TRACE_SB_LOCK */ +static inline void __lock_super(struct super_block *sb) +{ + lock_super(sb); +} + +static inline void __unlock_super(struct super_block *sb) +{ + unlock_super(sb); +} +#endif /* CONFIG_SDFAT_TRACE_SB_LOCK */ + +/************************************************************************* + * NORMAL FUNCTIONS + *************************************************************************/ +static inline loff_t sdfat_make_i_pos(FILE_ID_T *fid) +{ + return ((loff_t) fid->dir.dir << 32) | (fid->entry & 0xffffffff); +} + +/*======================================================================*/ +/* Directory Entry Name Buffer Operations */ +/*======================================================================*/ +static void sdfat_init_namebuf(DENTRY_NAMEBUF_T *nb) +{ + nb->lfn = NULL; + nb->sfn = NULL; + nb->lfnbuf_len = 0; + nb->sfnbuf_len = 0; +} + +static int sdfat_alloc_namebuf(DENTRY_NAMEBUF_T *nb) +{ + nb->lfn = __getname(); + if (!nb->lfn) + return -ENOMEM; + nb->sfn = nb->lfn + MAX_VFSNAME_BUF_SIZE; + nb->lfnbuf_len = MAX_VFSNAME_BUF_SIZE; + nb->sfnbuf_len = MAX_VFSNAME_BUF_SIZE; + return 0; +} + +static void sdfat_free_namebuf(DENTRY_NAMEBUF_T *nb) +{ + if (!nb->lfn) + return; + + __putname(nb->lfn); + sdfat_init_namebuf(nb); +} + +/*======================================================================*/ +/* Directory Entry Operations */ +/*======================================================================*/ +#define SDFAT_DSTATE_LOCKED (void *)(0xCAFE2016) +#define SDFAT_DSTATE_UNLOCKED (void *)(0x00000000) + +static inline void __lock_d_revalidate(struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + dentry->d_fsdata = SDFAT_DSTATE_LOCKED; + spin_unlock(&dentry->d_lock); +} + +static inline void __unlock_d_revalidate(struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + dentry->d_fsdata = SDFAT_DSTATE_UNLOCKED; + spin_unlock(&dentry->d_lock); +} + +/* __check_dstate_locked requires dentry->d_lock */ +static inline int __check_dstate_locked(struct dentry *dentry) +{ + if (dentry->d_fsdata == SDFAT_DSTATE_LOCKED) + return 1; + + return 0; +} + +/* + * If new entry was created in the parent, it could create the 8.3 + * alias (the shortname of logname). So, the parent may have the + * negative-dentry which matches the created 8.3 alias. + * + * If it happened, the negative dentry isn't actually negative + * anymore. So, drop it. + */ +static int __sdfat_revalidate_common(struct dentry *dentry) +{ + int ret = 1; + + spin_lock(&dentry->d_lock); + if ((!dentry->d_inode) && (!__check_dstate_locked(dentry) && + (dentry->d_time != dentry->d_parent->d_inode->i_version))) { + ret = 0; + } + spin_unlock(&dentry->d_lock); + return ret; +} + +static int __sdfat_revalidate(struct dentry *dentry) +{ + /* This is not negative dentry. Always valid. */ + if (dentry->d_inode) + return 1; + return __sdfat_revalidate_common(dentry); +} + +static int __sdfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +{ + /* + * This is not negative dentry. Always valid. + * + * Note, rename() to existing directory entry will have ->d_inode, + * and will use existing name which isn't specified name by user. + * + * We may be able to drop this positive dentry here. But dropping + * positive dentry isn't good idea. So it's unsupported like + * rename("filename", "FILENAME") for now. + */ + if (dentry->d_inode) + return 1; +#if 0 /* Blocked below code for lookup_one_len() called by stackable FS */ + /* + * This may be nfsd (or something), anyway, we can't see the + * intent of this. So, since this can be for creation, drop it. + */ + if (!flags) + return 0; +#endif + /* + * Drop the negative dentry, in order to make sure to use the + * case sensitive name which is specified by user if this is + * for creation. + */ + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + return __sdfat_revalidate_common(dentry); +} + + +/* returns the length of a struct qstr, ignoring trailing dots */ +static unsigned int __sdfat_striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +static unsigned int sdfat_striptail_len(const struct qstr *qstr) +{ + return __sdfat_striptail_len(qstr->len, qstr->name); +} + +/* + * Compute the hash for the sdfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The sdfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int __sdfat_d_hash(const struct dentry *dentry, struct qstr *qstr) +{ + unsigned int len = sdfat_striptail_len(qstr); + + qstr->hash = __sdfat_full_name_hash(dentry, qstr->name, len); + return 0; +} + +/* + * Compute the hash for the sdfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The sdfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int __sdfat_d_hashi(const struct dentry *dentry, struct qstr *qstr) +{ + struct nls_table *t = SDFAT_SB(dentry->d_sb)->nls_io; + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + len = sdfat_striptail_len(qstr); + + hash = __sdfat_init_name_hash(dentry); + while (len--) + hash = partial_name_hash(nls_tolower(t, *name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Case sensitive compare of two sdfat names. + */ +static int __sdfat_cmp(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = sdfat_striptail_len(name); + blen = __sdfat_striptail_len(len, str); + if (alen == blen) { + if (strncmp(name->name, str, alen) == 0) + return 0; + } + return 1; +} + +/* + * Case insensitive compare of two sdfat names. + */ +static int __sdfat_cmpi(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + struct nls_table *t = SDFAT_SB(dentry->d_sb)->nls_io; + unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = sdfat_striptail_len(name); + blen = __sdfat_striptail_len(len, str); + if (alen == blen) { + if (nls_strnicmp(t, name->name, str, alen) == 0) + return 0; + } + return 1; +} + +static const struct dentry_operations sdfat_dentry_ops = { + .d_revalidate = sdfat_revalidate, + .d_hash = sdfat_d_hash, + .d_compare = sdfat_cmp, +}; + +static const struct dentry_operations sdfat_ci_dentry_ops = { + .d_revalidate = sdfat_revalidate_ci, + .d_hash = sdfat_d_hashi, + .d_compare = sdfat_cmpi, +}; + +#ifdef CONFIG_SDFAT_DFR +/*----------------------------------------------------------------------*/ +/* Defragmentation related */ +/*----------------------------------------------------------------------*/ +/** + * @fn defrag_cleanup_reqs + * @brief clean-up defrag info depending on error flag + * @return void + * @param sb super block + * @param error error flag + */ +static void defrag_cleanup_reqs(INOUT struct super_block *sb, IN int error) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct defrag_info *sb_dfr = &(sbi->dfr_info); + struct defrag_info *ino_dfr = NULL, *tmp = NULL; + /* sdfat patch 0.96 : sbi->dfr_info crash problem */ + __lock_super(sb); + + /* Clean-up ino_dfr */ + if (!error) { + list_for_each_entry_safe(ino_dfr, tmp, &sb_dfr->entry, entry) { + struct inode *inode = &(container_of(ino_dfr, struct sdfat_inode_info, dfr_info)->vfs_inode); + + mutex_lock(&ino_dfr->lock); + + atomic_set(&ino_dfr->stat, DFR_INO_STAT_IDLE); + + list_del(&ino_dfr->entry); + + ino_dfr->chunks = NULL; + ino_dfr->nr_chunks = 0; + INIT_LIST_HEAD(&ino_dfr->entry); + + BUG_ON(!mutex_is_locked(&ino_dfr->lock)); + mutex_unlock(&ino_dfr->lock); + + iput(inode); + } + } + + /* Clean-up sb_dfr */ + sb_dfr->chunks = NULL; + sb_dfr->nr_chunks = 0; + INIT_LIST_HEAD(&sb_dfr->entry); + + /* Clear dfr_new_clus page */ + memset(sbi->dfr_new_clus, 0, PAGE_SIZE); + sbi->dfr_new_idx = 1; + memset(sbi->dfr_page_wb, 0, PAGE_SIZE); + + sbi->dfr_hint_clus = sbi->dfr_hint_idx = sbi->dfr_reserved_clus = 0; + + __unlock_super(sb); +} + +/** + * @fn defrag_validate_pages + * @brief validate and mark dirty for victiim pages + * @return 0 on success, -errno otherwise + * @param inode inode + * @param chunk given chunk + * @remark protected by inode_lock and super_lock + */ +static int +defrag_validate_pages( + IN struct inode *inode, + IN struct defrag_chunk_info *chunk) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct page *page = NULL; + unsigned int i_size = 0, page_off = 0, page_nr = 0; + int buf_i = 0, i = 0, err = 0; + + i_size = i_size_read(inode); + page_off = chunk->f_clus * PAGES_PER_CLUS(sb); + page_nr = (i_size / PAGE_SIZE) + ((i_size % PAGE_SIZE) ? 1 : 0); + if ((i_size <= 0) || (page_nr <= 0)) { + dfr_err("inode %p, i_size %d, page_nr %d", inode, i_size, page_nr); + return -EINVAL; + } + + /* Get victim pages + * and check its dirty/writeback/mapped state + */ + for (i = 0; + i < min((int)(page_nr - page_off), (int)(chunk->nr_clus * PAGES_PER_CLUS(sb))); + i++) { + page = find_get_page(inode->i_mapping, page_off + i); + if (page) + if (!trylock_page(page)) { + put_page(page); + page = NULL; + } + + if (!page) { + dfr_debug("get/lock_page() failed, index %d", i); + err = -EINVAL; + goto error; + } + + sbi->dfr_pagep[buf_i++] = page; + if (PageError(page) || !PageUptodate(page) || PageDirty(page) || + PageWriteback(page) || page_mapped(page)) { + dfr_debug("page %p, err %d, uptodate %d, " + "dirty %d, wb %d, mapped %d", + page, PageError(page), PageUptodate(page), + PageDirty(page), PageWriteback(page), + page_mapped(page)); + err = -EINVAL; + goto error; + } + + set_bit((page->index & (PAGES_PER_CLUS(sb) - 1)), + (volatile unsigned long *)&(sbi->dfr_page_wb[chunk->new_idx + i / PAGES_PER_CLUS(sb)])); + + page = NULL; + } + + /** + * All pages in the chunks are valid. + */ + i_size -= (chunk->f_clus * (sbi->fsi.cluster_size)); + BUG_ON(((i_size / PAGE_SIZE) + ((i_size % PAGE_SIZE) ? 1 : 0)) != (page_nr - page_off)); + + for (i = 0; i < buf_i; i++) { + struct buffer_head *bh = NULL, *head = NULL; + int bh_idx = 0; + + page = sbi->dfr_pagep[i]; + BUG_ON(!page); + + /* Mark dirty in page */ + set_page_dirty(page); + mark_page_accessed(page); + + /* Attach empty BHs */ + if (!page_has_buffers(page)) + create_empty_buffers(page, 1 << inode->i_blkbits, 0); + + /* Mark dirty in BHs */ + bh = head = page_buffers(page); + BUG_ON(!bh && !i_size); + do { + if ((bh_idx >= 1) && (bh_idx >= (i_size >> inode->i_blkbits))) { + clear_buffer_dirty(bh); + } else { + if (PageUptodate(page)) + if (!buffer_uptodate(bh)) + set_buffer_uptodate(bh); + + /* Set this bh as delay */ + set_buffer_new(bh); + set_buffer_delay(bh); + + mark_buffer_dirty(bh); + } + + bh_idx++; + bh = bh->b_this_page; + } while (bh != head); + + /* Mark this page accessed */ + mark_page_accessed(page); + + i_size -= PAGE_SIZE; + } + +error: + /* Unlock and put refs for pages */ + for (i = 0; i < buf_i; i++) { + BUG_ON(!sbi->dfr_pagep[i]); + unlock_page(sbi->dfr_pagep[i]); + put_page(sbi->dfr_pagep[i]); + } + memset(sbi->dfr_pagep, 0, sizeof(PAGE_SIZE)); + + return err; +} + + +/** + * @fn defrag_validate_reqs + * @brief validate defrag requests + * @return negative if all requests not valid, 0 otherwise + * @param sb super block + * @param chunks given chunks + */ +static int +defrag_validate_reqs( + IN struct super_block *sb, + INOUT struct defrag_chunk_info *chunks) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct defrag_info *sb_dfr = &(sbi->dfr_info); + int i = 0, err = 0, err_cnt = 0; + + /* Validate all reqs */ + for (i = REQ_HEADER_IDX + 1; i < sb_dfr->nr_chunks; i++) { + struct defrag_chunk_info *chunk = NULL; + struct inode *inode = NULL; + struct defrag_info *ino_dfr = NULL; + + chunk = &chunks[i]; + + /* Check inode */ + __lock_super(sb); + inode = sdfat_iget(sb, chunk->i_pos); + if (!inode) { + dfr_debug("inode not found, i_pos %08llx", chunk->i_pos); + chunk->stat = DFR_CHUNK_STAT_ERR; + err_cnt++; + __unlock_super(sb); + continue; + } + __unlock_super(sb); + + dfr_debug("req[%d] inode %p, i_pos %08llx, f_clus %d, " + "d_clus %08x, nr %d, prev %08x, next %08x", + i, inode, chunk->i_pos, chunk->f_clus, chunk->d_clus, + chunk->nr_clus, chunk->prev_clus, chunk->next_clus); + /** + * Lock ordering: inode_lock -> lock_super + */ + inode_lock(inode); + __lock_super(sb); + + /* Check if enough buffers exist for chunk->new_idx */ + if ((sbi->dfr_new_idx + chunk->nr_clus) >= (PAGE_SIZE / sizeof(int))) { + dfr_err("dfr_new_idx %d, chunk->nr_clus %d", + sbi->dfr_new_idx, chunk->nr_clus); + err = -ENOSPC; + goto unlock; + } + + /* Reserve clusters for defrag with DA */ + err = fsapi_dfr_reserve_clus(sb, chunk->nr_clus); + if (err) + goto unlock; + + /* Check clusters */ + err = fsapi_dfr_validate_clus(inode, chunk, 0); + if (err) { + fsapi_dfr_reserve_clus(sb, 0 - chunk->nr_clus); + dfr_debug("Cluster validation: err %d", err); + goto unlock; + } + + /* Check pages */ + err = defrag_validate_pages(inode, chunk); + if (err) { + fsapi_dfr_reserve_clus(sb, 0 - chunk->nr_clus); + dfr_debug("Page validation: err %d", err); + goto unlock; + } + + /* Mark IGNORE flag to victim AU */ + if (sbi->options.improved_allocation & SDFAT_ALLOC_SMART) + fsapi_dfr_mark_ignore(sb, chunk->d_clus); + + ino_dfr = &(SDFAT_I(inode)->dfr_info); + mutex_lock(&ino_dfr->lock); + + /* Update chunk info */ + chunk->stat = DFR_CHUNK_STAT_REQ; + chunk->new_idx = sbi->dfr_new_idx; + + /* Update ino_dfr info */ + if (list_empty(&(ino_dfr->entry))) { + list_add_tail(&ino_dfr->entry, &sb_dfr->entry); + ino_dfr->chunks = chunk; + igrab(inode); + } + ino_dfr->nr_chunks++; + + atomic_set(&ino_dfr->stat, DFR_INO_STAT_REQ); + + BUG_ON(!mutex_is_locked(&ino_dfr->lock)); + mutex_unlock(&ino_dfr->lock); + + /* Reserved buffers for chunk->new_idx */ + sbi->dfr_new_idx += chunk->nr_clus; + +unlock: + if (err) { + chunk->stat = DFR_CHUNK_STAT_ERR; + err_cnt++; + } + iput(inode); + __unlock_super(sb); + inode_unlock(inode); + } + + /* Return error if all chunks are invalid */ + if (err_cnt == sb_dfr->nr_chunks - 1) { + dfr_debug("%s failed (err_cnt %d)", __func__, err_cnt); + return -ENXIO; + } + + return 0; +} + + +/** + * @fn defrag_check_fs_busy + * @brief check if this module busy + * @return 0 when idle, 1 otherwise + * @param sb super block + * @param reserved_clus # of reserved clusters + * @param queued_pages # of queued pages + */ +static int +defrag_check_fs_busy( + IN struct super_block *sb, + OUT int *reserved_clus, + OUT int *queued_pages) +{ + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + int err = 0; + + *reserved_clus = *queued_pages = 0; + + __lock_super(sb); + *reserved_clus = fsi->reserved_clusters; + *queued_pages = atomic_read(&SDFAT_SB(sb)->stat_n_pages_queued); + + if (*reserved_clus || *queued_pages) + err = 1; + __unlock_super(sb); + + return err; +} + + +/** + * @fn sdfat_ioctl_defrag_req + * @brief ioctl to send defrag requests + * @return 0 on success, -errno otherwise + * @param inode inode + * @param uarg given requests + */ +static int +sdfat_ioctl_defrag_req( + IN struct inode *inode, + INOUT unsigned int *uarg) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct defrag_info *sb_dfr = &(sbi->dfr_info); + struct defrag_chunk_header head; + struct defrag_chunk_info *chunks = NULL; + unsigned int len = 0; + int err = 0; + unsigned long timeout = 0; + + /* Check overlapped defrag */ + if (atomic_cmpxchg(&sb_dfr->stat, DFR_SB_STAT_IDLE, DFR_SB_STAT_REQ)) { + dfr_debug("sb_dfr->stat %d", atomic_read(&sb_dfr->stat)); + return -EBUSY; + } + + /* Check if defrag required */ + __lock_super(sb); + if (!fsapi_dfr_check_dfr_required(sb, NULL, NULL, NULL)) { + dfr_debug("Not enough space left for defrag (err %d)", -ENOSPC); + atomic_set(&sb_dfr->stat, DFR_SB_STAT_IDLE); + __unlock_super(sb); + return -ENOSPC; + } + __unlock_super(sb); + + /* Copy args */ + memset(&head, 0, sizeof(struct defrag_chunk_header)); + err = copy_from_user(&head, uarg, sizeof(struct defrag_chunk_info)); + ERR_HANDLE(err); + + /* If FS busy, cancel defrag */ + if (!(head.mode == DFR_MODE_TEST)) { + int reserved_clus = 0, queued_pages = 0; + + err = defrag_check_fs_busy(sb, &reserved_clus, &queued_pages); + if (err) { + dfr_debug("FS busy, cancel defrag (reserved_clus %d, queued_pages %d)", + reserved_clus, queued_pages); + err = -EBUSY; + goto error; + } + } + + /* Total length is saved in the chunk header's nr_chunks field */ + len = head.nr_chunks; + ERR_HANDLE2(!len, err, -EINVAL); + + dfr_debug("IOC_DFR_REQ started (mode %d, nr_req %d)", head.mode, len - 1); + if (get_order(len * sizeof(struct defrag_chunk_info)) > MAX_ORDER) { + dfr_debug("len %d, sizeof(struct defrag_chunk_info) %lu, MAX_ORDER %d", + len, sizeof(struct defrag_chunk_info), MAX_ORDER); + err = -EINVAL; + goto error; + } + chunks = alloc_pages_exact(len * sizeof(struct defrag_chunk_info), + GFP_KERNEL | __GFP_ZERO); + ERR_HANDLE2(!chunks, err, -ENOMEM) + + err = copy_from_user(chunks, uarg, len * sizeof(struct defrag_chunk_info)); + ERR_HANDLE(err); + + /* Initialize sb_dfr */ + sb_dfr->chunks = chunks; + sb_dfr->nr_chunks = len; + + /* Validate reqs & mark defrag/dirty */ + err = defrag_validate_reqs(sb, sb_dfr->chunks); + ERR_HANDLE(err); + + atomic_set(&sb_dfr->stat, DFR_SB_STAT_VALID); + + /* Wait for defrag completion */ + if (head.mode == DFR_MODE_ONESHOT) + timeout = 0; + else if (head.mode & DFR_MODE_BACKGROUND) + timeout = DFR_DEFAULT_TIMEOUT; + else + timeout = DFR_MIN_TIMEOUT; + + dfr_debug("Wait for completion (timeout %ld)", timeout); + init_completion(&sbi->dfr_complete); + timeout = wait_for_completion_timeout(&sbi->dfr_complete, timeout); + + if (!timeout) { + /* Force defrag_updat_fat() after timeout. */ + dfr_debug("Force sync(), mode %d, left-timeout %ld", head.mode, timeout); + + down_read(&sb->s_umount); + + sync_inodes_sb(sb); + + __lock_super(sb); + fsapi_dfr_update_fat_next(sb); + + fsapi_sync_fs(sb, 1); + +#ifdef CONFIG_SDFAT_DFR_DEBUG + /* SPO test */ + fsapi_dfr_spo_test(sb, DFR_SPO_FAT_NEXT, __func__); +#endif + + fsapi_dfr_update_fat_prev(sb, 1); + fsapi_sync_fs(sb, 1); + + __unlock_super(sb); + + up_read(&sb->s_umount); + } + +#ifdef CONFIG_SDFAT_DFR_DEBUG + /* SPO test */ + fsapi_dfr_spo_test(sb, DFR_SPO_NORMAL, __func__); +#endif + + __lock_super(sb); + /* Send DISCARD to clean-ed AUs */ + fsapi_dfr_check_discard(sb); + +#ifdef CONFIG_SDFAT_DFR_DEBUG + /* SPO test */ + fsapi_dfr_spo_test(sb, DFR_SPO_DISCARD, __func__); +#endif + + /* Unmark IGNORE flag to all victim AUs */ + fsapi_dfr_unmark_ignore_all(sb); + __unlock_super(sb); + + err = copy_to_user(uarg, sb_dfr->chunks, sizeof(struct defrag_chunk_info) * len); + ERR_HANDLE(err); + +error: + /* Clean-up sb_dfr & ino_dfr */ + defrag_cleanup_reqs(sb, err); + + if (chunks) + free_pages_exact(chunks, len * sizeof(struct defrag_chunk_info)); + + /* Set sb_dfr's state as IDLE */ + atomic_set(&sb_dfr->stat, DFR_SB_STAT_IDLE); + + dfr_debug("IOC_DFR_REQ done (err %d)", err); + return err; +} + +/** + * @fn sdfat_ioctl_defrag_trav + * @brief ioctl to traverse given directory for defrag + * @return 0 on success, -errno otherwise + * @param inode inode + * @param uarg output buffer + */ +static int +sdfat_ioctl_defrag_trav( + IN struct inode *inode, + INOUT unsigned int *uarg) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct defrag_info *sb_dfr = &(sbi->dfr_info); + struct defrag_trav_arg *args = (struct defrag_trav_arg *) sbi->dfr_pagep; + struct defrag_trav_header *header = (struct defrag_trav_header *) args; + int err = 0; + + /* Check overlapped defrag */ + if (atomic_cmpxchg(&sb_dfr->stat, DFR_SB_STAT_IDLE, DFR_SB_STAT_REQ)) { + dfr_debug("sb_dfr->stat %d", atomic_read(&sb_dfr->stat)); + return -EBUSY; + } + + /* Check if defrag required */ + __lock_super(sb); + if (!fsapi_dfr_check_dfr_required(sb, NULL, NULL, NULL)) { + dfr_debug("Not enough space left for defrag (err %d)", -ENOSPC); + atomic_set(&sb_dfr->stat, DFR_SB_STAT_IDLE); + __unlock_super(sb); + return -ENOSPC; + } + __unlock_super(sb); + + /* Copy args */ + err = copy_from_user(args, uarg, PAGE_SIZE); + ERR_HANDLE(err); + + /** + * Check args. + * ROOT directory has i_pos = 0 and start_clus = 0 . + */ + if (!(header->type & DFR_TRAV_TYPE_HEADER)) { + err = -EINVAL; + dfr_debug("type %d, i_pos %08llx, start_clus %08x", + header->type, header->i_pos, header->start_clus); + goto error; + } + + /* If FS busy, cancel defrag */ + if (!(header->type & DFR_TRAV_TYPE_TEST)) { + unsigned int reserved_clus = 0, queued_pages = 0; + + err = defrag_check_fs_busy(sb, &reserved_clus, &queued_pages); + if (err) { + dfr_debug("FS busy, cancel defrag (reserved_clus %d, queued_pages %d)", + reserved_clus, queued_pages); + err = -EBUSY; + goto error; + } + } + + /* Scan given directory and gather info */ + inode_lock(inode); + __lock_super(sb); + err = fsapi_dfr_scan_dir(sb, (void *)args); + __unlock_super(sb); + inode_unlock(inode); + ERR_HANDLE(err); + + /* Copy the result to user */ + err = copy_to_user(uarg, args, PAGE_SIZE); + ERR_HANDLE(err); + +error: + memset(sbi->dfr_pagep, 0, PAGE_SIZE); + + atomic_set(&sb_dfr->stat, DFR_SB_STAT_IDLE); + return err; +} + +/** + * @fn sdfat_ioctl_defrag_info + * @brief ioctl to get HW param info + * @return 0 on success, -errno otherwise + * @param sb super block + * @param uarg output buffer + */ +static int +sdfat_ioctl_defrag_info( + IN struct super_block *sb, + OUT unsigned int *uarg) +{ + struct defrag_info_arg info_arg; + int err = 0; + + memset(&info_arg, 0, sizeof(struct defrag_info_arg)); + + __lock_super(sb); + err = fsapi_dfr_get_info(sb, &info_arg); + __unlock_super(sb); + ERR_HANDLE(err); + dfr_debug("IOC_DFR_INFO: sec_per_au %d, hidden_sectors %d", + info_arg.sec_per_au, info_arg.hidden_sectors); + + err = copy_to_user(uarg, &info_arg, sizeof(struct defrag_info_arg)); +error: + return err; +} + +#endif /* CONFIG_SDFAT_DFR */ + +static inline int __do_dfr_map_cluster(struct inode *inode, u32 clu_offset, unsigned int *clus_ptr) +{ +#ifdef CONFIG_SDFAT_DFR + return fsapi_dfr_map_clus(inode, clu_offset, clus_ptr); +#else + return 0; +#endif +} + +static inline int __check_dfr_on(struct inode *inode, loff_t start, loff_t end, const char *fname) +{ +#ifdef CONFIG_SDFAT_DFR + struct defrag_info *ino_dfr = &(SDFAT_I(inode)->dfr_info); + + if ((atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) && + fsapi_dfr_check_dfr_on(inode, start, end, 0, fname)) + return 1; +#endif + return 0; +} + +static inline int __cancel_dfr_work(struct inode *inode, loff_t start, loff_t end, const char *fname) +{ +#ifdef CONFIG_SDFAT_DFR + struct defrag_info *ino_dfr = &(SDFAT_I(inode)->dfr_info); + /* Cancel DEFRAG */ + if (atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) + fsapi_dfr_check_dfr_on(inode, start, end, 1, fname); +#endif + return 0; +} + +static inline int __dfr_writepage_end_io(struct page *page) +{ +#ifdef CONFIG_SDFAT_DFR + struct defrag_info *ino_dfr = &(SDFAT_I(page->mapping->host)->dfr_info); + + if (atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) + fsapi_dfr_writepage_endio(page); +#endif + return 0; +} + +static inline void __init_dfr_info(struct inode *inode) +{ +#ifdef CONFIG_SDFAT_DFR + memset(&(SDFAT_I(inode)->dfr_info), 0, sizeof(struct defrag_info)); + INIT_LIST_HEAD(&(SDFAT_I(inode)->dfr_info.entry)); + mutex_init(&(SDFAT_I(inode)->dfr_info.lock)); +#endif +} + +static inline int __alloc_dfr_mem_if_required(struct super_block *sb) +{ +#ifdef CONFIG_SDFAT_DFR + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + if (!sbi->options.defrag) + return 0; + + memset(&sbi->dfr_info, 0, sizeof(struct defrag_info)); + INIT_LIST_HEAD(&(sbi->dfr_info.entry)); + mutex_init(&(sbi->dfr_info.lock)); + + sbi->dfr_new_clus = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!sbi->dfr_new_clus) { + dfr_debug("error %d", -ENOMEM); + return -ENOMEM; + } + sbi->dfr_new_idx = 1; + + sbi->dfr_page_wb = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!sbi->dfr_page_wb) { + dfr_debug("error %d", -ENOMEM); + return -ENOMEM; + } + + sbi->dfr_pagep = alloc_pages_exact(sizeof(struct page *) * + PAGES_PER_AU(sb), GFP_KERNEL | __GFP_ZERO); + if (!sbi->dfr_pagep) { + dfr_debug("error %d", -ENOMEM); + return -ENOMEM; + } +#endif + return 0; +} + +static void __free_dfr_mem_if_required(struct super_block *sb) +{ +#ifdef CONFIG_SDFAT_DFR + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + if (sbi->dfr_pagep) { + free_pages_exact(sbi->dfr_pagep, sizeof(struct page *) * PAGES_PER_AU(sb)); + sbi->dfr_pagep = NULL; + } + + /* thanks for kfree */ + kfree(sbi->dfr_page_wb); + sbi->dfr_page_wb = NULL; + + kfree(sbi->dfr_new_clus); + sbi->dfr_new_clus = NULL; +#endif +} + + +static int sdfat_file_mmap(struct file *file, struct vm_area_struct *vm_struct) +{ + __cancel_dfr_work(file->f_mapping->host, + (loff_t)vm_struct->vm_start, + (loff_t)(vm_struct->vm_end - 1), + __func__); + + return generic_file_mmap(file, vm_struct); +} + +static int sdfat_ioctl_volume_id(struct inode *dir) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(dir->i_sb); + FS_INFO_T *fsi = &(sbi->fsi); + + return fsi->vol_id; +} + +static int sdfat_dfr_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ +#ifdef CONFIG_SDFAT_DFR + switch (cmd) { + case SDFAT_IOCTL_DFR_INFO: { + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &SDFAT_SB(sb)->fsi; + unsigned int __user *uarg = (unsigned int __user *) arg; + + __lock_super(sb); + /* Check FS type (FAT32 only) */ + if (fsi->vol_type != FAT32) { + dfr_err("Defrag not supported, vol_type %d", fsi->vol_type); + __unlock_super(sb); + return -EPERM; + + } + + /* Check if SB's defrag option enabled */ + if (!(SDFAT_SB(sb)->options.defrag)) { + dfr_err("Defrag not supported, sbi->options.defrag %d", SDFAT_SB(sb)->options.defrag); + __unlock_super(sb); + return -EPERM; + } + + /* Only IOCTL on mount-point allowed */ + if (filp->f_path.mnt->mnt_root != filp->f_path.dentry) { + dfr_err("IOC_DFR_INFO only allowed on ROOT, root %p, dentry %p", + filp->f_path.mnt->mnt_root, filp->f_path.dentry); + __unlock_super(sb); + return -EPERM; + } + __unlock_super(sb); + + return sdfat_ioctl_defrag_info(sb, uarg); + } + case SDFAT_IOCTL_DFR_TRAV: { + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &SDFAT_SB(sb)->fsi; + unsigned int __user *uarg = (unsigned int __user *) arg; + + __lock_super(sb); + /* Check FS type (FAT32 only) */ + if (fsi->vol_type != FAT32) { + dfr_err("Defrag not supported, vol_type %d", fsi->vol_type); + __unlock_super(sb); + return -EPERM; + + } + + /* Check if SB's defrag option enabled */ + if (!(SDFAT_SB(sb)->options.defrag)) { + dfr_err("Defrag not supported, sbi->options.defrag %d", SDFAT_SB(sb)->options.defrag); + __unlock_super(sb); + return -EPERM; + } + __unlock_super(sb); + + return sdfat_ioctl_defrag_trav(inode, uarg); + } + case SDFAT_IOCTL_DFR_REQ: { + struct super_block *sb = inode->i_sb; + FS_INFO_T *fsi = &SDFAT_SB(sb)->fsi; + unsigned int __user *uarg = (unsigned int __user *) arg; + + __lock_super(sb); + + /* Check if FS_ERROR occurred */ + if (sb->s_flags & MS_RDONLY) { + dfr_err("RDONLY partition (err %d)", -EPERM); + __unlock_super(sb); + return -EPERM; + } + + /* Check FS type (FAT32 only) */ + if (fsi->vol_type != FAT32) { + dfr_err("Defrag not supported, vol_type %d", fsi->vol_type); + __unlock_super(sb); + return -EINVAL; + + } + + /* Check if SB's defrag option enabled */ + if (!(SDFAT_SB(sb)->options.defrag)) { + dfr_err("Defrag not supported, sbi->options.defrag %d", SDFAT_SB(sb)->options.defrag); + __unlock_super(sb); + return -EPERM; + } + + /* Only IOCTL on mount-point allowed */ + if (filp->f_path.mnt->mnt_root != filp->f_path.dentry) { + dfr_err("IOC_DFR_INFO only allowed on ROOT, root %p, dentry %p", + filp->f_path.mnt->mnt_root, filp->f_path.dentry); + __unlock_super(sb); + return -EINVAL; + } + __unlock_super(sb); + + return sdfat_ioctl_defrag_req(inode, uarg); + } +#ifdef CONFIG_SDFAT_DFR_DEBUG + case SDFAT_IOCTL_DFR_SPO_FLAG: { + struct sdfat_sb_info *sbi = SDFAT_SB(inode->i_sb); + int ret = 0; + + ret = get_user(sbi->dfr_spo_flag, (int __user *)arg); + dfr_debug("dfr_spo_flag %d", sbi->dfr_spo_flag); + + return ret; + } +#endif /* CONFIG_SDFAT_DFR_DEBUG */ + } +#endif /* CONFIG_SDFAT_DFR */ + + /* Inappropriate ioctl for device */ + return -ENOTTY; +} + +static int sdfat_dbg_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ +#ifdef CONFIG_SDFAT_DBG_IOCTL + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + unsigned int flags; + + switch (cmd) { + case SDFAT_IOC_GET_DEBUGFLAGS: + flags = sbi->debug_flags; + return put_user(flags, (int __user *)arg); + case SDFAT_IOC_SET_DEBUGFLAGS: + flags = 0; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (int __user *) arg)) + return -EFAULT; + + __lock_super(sb); + sbi->debug_flags = flags; + __unlock_super(sb); + return 0; + case SDFAT_IOCTL_PANIC: + panic("ioctl panic for test"); + + /* COULD NOT REACH HEAR */ + return 0; + } +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + return -ENOTTY; +} + +static long sdfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + int err; + + if (cmd == SDFAT_IOCTL_GET_VOLUME_ID) + return sdfat_ioctl_volume_id(inode); + + err = sdfat_dfr_ioctl(inode, filp, cmd, arg); + if (err != -ENOTTY) + return err; + + /* -ENOTTY if inappropriate ioctl for device */ + return sdfat_dbg_ioctl(inode, filp, cmd, arg); +} + + +static void __sdfat_writepage_end_io(struct bio *bio, int err) +{ + struct page *page = bio->bi_io_vec->bv_page; + struct super_block *sb = page->mapping->host->i_sb; + + ASSERT(bio->bi_vcnt == 1); /* Single page endio */ + ASSERT(bio_data_dir(bio)); /* Write */ + + if (err) { + SetPageError(page); + mapping_set_error(page->mapping, err); + } + + __dfr_writepage_end_io(page); + +#ifdef CONFIG_SDFAT_TRACE_IO + { + //struct sdfat_sb_info *sbi = SDFAT_SB(bio->bi_bdev->bd_super); + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + sbi->stat_n_pages_written++; + if (page->mapping->host == sb->s_bdev->bd_inode) + sbi->stat_n_bdev_pages_written++; + + /* 4 MB = 1024 pages => 0.4 sec (approx.) + * 32 KB = 64 pages => 0.025 sec + * Min. average latency b/w msgs. ~= 0.025 sec + */ + if ((sbi->stat_n_pages_written & 63) == 0) { + DMSG("STAT:%u, %u, %u, %u (Sector #: %u)\n", + sbi->stat_n_pages_added, sbi->stat_n_pages_written, + sbi->stat_n_bdev_pages_witten, + sbi->stat_n_pages_confused, + (unsigned int)__sdfat_bio_sector(bio)); + } + } +#endif + end_page_writeback(page); + bio_put(bio); + + // Update trace info. + atomic_dec(&SDFAT_SB(sb)->stat_n_pages_queued); +} + + +static int __support_write_inode_sync(struct super_block *sb) +{ +#ifdef CONFIG_SDFAT_SUPPORT_DIR_SYNC +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + if (sbi->fsi.vol_type != EXFAT) + return 0; +#endif + return 1; +#endif + return 0; +} + + +static int __sdfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) +{ + struct inode *inode = filp->f_mapping->host; + struct super_block *sb = inode->i_sb; + int res, err = 0; + + res = __sdfat_generic_file_fsync(filp, start, end, datasync); + + if (!__support_write_inode_sync(sb)) + err = fsapi_sync_fs(sb, 1); + + return res ? res : err; +} + + +static const struct file_operations sdfat_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + .iterate = sdfat_iterate, +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */ + .readdir = sdfat_readdir, +#endif + .fsync = sdfat_file_fsync, + .unlocked_ioctl = sdfat_generic_ioctl, +}; + +static int __sdfat_create(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + TMSG("%s entered\n", __func__); + + err = fsapi_create(dir, (u8 *) dentry->d_name.name, FM_REGULAR, &fid); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) sdfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = sdfat_make_i_pos(&fid); + inode = sdfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + d_instantiate(dentry, inode); +out: + __unlock_d_revalidate(dentry); + __unlock_super(sb); + TMSG("%s exited with err(%d)\n", __func__, err); + if (!err) + sdfat_statistics_set_create(fid.flags); + return err; +} + + +static int sdfat_find(struct inode *dir, struct qstr *qname, FILE_ID_T *fid) +{ + int err; + + if (qname->len == 0) + return -ENOENT; + + err = fsapi_lookup(dir, (u8 *) qname->name, fid); + if (err) + return -ENOENT; + + return 0; +} + +static int sdfat_d_anon_disconn(struct dentry *dentry) +{ + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); +} + +static struct dentry *__sdfat_lookup(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct dentry *alias; + int err; + FILE_ID_T fid; + loff_t i_pos; + u64 ret; + mode_t i_mode; + + __lock_super(sb); + TMSG("%s entered\n", __func__); + err = sdfat_find(dir, &dentry->d_name, &fid); + if (err) { + if (err == -ENOENT) { + inode = NULL; + goto out; + } + goto error; + } + + i_pos = sdfat_make_i_pos(&fid); + inode = sdfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto error; + } + + i_mode = inode->i_mode; + if (S_ISLNK(i_mode) && !SDFAT_I(inode)->target) { + SDFAT_I(inode)->target = kmalloc((i_size_read(inode)+1), GFP_KERNEL); + if (!SDFAT_I(inode)->target) { + err = -ENOMEM; + goto error; + } + fsapi_read_link(dir, &fid, SDFAT_I(inode)->target, i_size_read(inode), &ret); + *(SDFAT_I(inode)->target + i_size_read(inode)) = '\0'; + } + + alias = d_find_alias(inode); + + /* + * Checking "alias->d_parent == dentry->d_parent" to make sure + * FS is not corrupted (especially double linked dir). + */ + if (alias && alias->d_parent == dentry->d_parent && + !sdfat_d_anon_disconn(alias)) { + + /* + * Unhashed alias is able to exist because of revalidate() + * called by lookup_fast. You can easily make this status + * by calling create and lookup concurrently + * In such case, we reuse an alias instead of new dentry + */ + if (d_unhashed(alias)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + BUG_ON(alias->d_name.hash != dentry->d_name.hash && alias->d_name.len != dentry->d_name.len); +#else + BUG_ON(alias->d_name.hash_len != dentry->d_name.hash_len); +#endif + sdfat_msg(sb, KERN_INFO, "rehashed a dentry(%p) " + "in read lookup", alias); + d_drop(dentry); + d_rehash(alias); + } else if (!S_ISDIR(i_mode)) { + /* + * This inode has non anonymous-DCACHE_DISCONNECTED + * dentry. This means, the user did ->lookup() by an + * another name (longname vs 8.3 alias of it) in past. + * + * Switch to new one for reason of locality if possible. + */ + d_move(alias, dentry); + } + iput(inode); + __unlock_super(sb); + TMSG("%s exited\n", __func__); + return alias; + } + dput(alias); +out: + /* initialize d_time even though it is positive dentry */ + dentry->d_time = dir->i_version; + __unlock_super(sb); + + dentry = d_splice_alias(inode, dentry); + + TMSG("%s exited\n", __func__); + return dentry; +error: + __unlock_super(sb); + TMSG("%s exited with err(%d)\n", __func__, err); + return ERR_PTR(err); +} + + +static int sdfat_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + int err; + + __lock_super(sb); + + TMSG("%s entered\n", __func__); + + SDFAT_I(inode)->fid.size = i_size_read(inode); + + __cancel_dfr_work(inode, 0, SDFAT_I(inode)->fid.size, __func__); + + err = fsapi_unlink(dir, &(SDFAT_I(inode)->fid)); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + dir->i_version++; + dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) sdfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = current_time(inode); + sdfat_detach(inode); + dentry->d_time = dir->i_version; +out: + __unlock_d_revalidate(dentry); + __unlock_super(sb); + TMSG("%s exited with err(%d)\n", __func__, err); + return err; +} + +static int sdfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + FILE_ID_T fid; + loff_t i_pos; + int err; + u64 len = (u64) strlen(target); + u64 ret; + + /* symlink option check */ + if (!SDFAT_SB(sb)->options.symlink) + return -ENOTSUPP; + + __lock_super(sb); + + TMSG("%s entered\n", __func__); + + err = fsapi_create(dir, (u8 *) dentry->d_name.name, FM_SYMLINK, &fid); + if (err) + goto out; + + err = fsapi_write_link(dir, &fid, (char *) target, len, &ret); + + if (err) { + fsapi_remove(dir, &fid); + goto out; + } + + __lock_d_revalidate(dentry); + + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) sdfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = sdfat_make_i_pos(&fid); + inode = sdfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + SDFAT_I(inode)->target = kmalloc((len+1), GFP_KERNEL); + if (!SDFAT_I(inode)->target) { + err = -ENOMEM; + goto out; + } + memcpy(SDFAT_I(inode)->target, target, len+1); + + d_instantiate(dentry, inode); +out: + __unlock_d_revalidate(dentry); + __unlock_super(sb); + TMSG("%s exited with err(%d)\n", __func__, err); + return err; +} + + +static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + TMSG("%s entered\n", __func__); + + err = fsapi_mkdir(dir, (u8 *) dentry->d_name.name, &fid); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) sdfat_sync_inode(dir); + else + mark_inode_dirty(dir); + inc_nlink(dir); + + i_pos = sdfat_make_i_pos(&fid); + inode = sdfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + d_instantiate(dentry, inode); + +out: + __unlock_d_revalidate(dentry); + __unlock_super(sb); + TMSG("%s exited with err(%d)\n", __func__, err); + if (!err) + sdfat_statistics_set_mkdir(fid.flags); + return err; +} + + +static int sdfat_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + int err; + + __lock_super(sb); + + TMSG("%s entered\n", __func__); + + SDFAT_I(inode)->fid.size = i_size_read(inode); + + err = fsapi_rmdir(dir, &(SDFAT_I(inode)->fid)); + if (err) + goto out; + + __lock_d_revalidate(dentry); + + dir->i_version++; + dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) sdfat_sync_inode(dir); + else + mark_inode_dirty(dir); + drop_nlink(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = current_time(inode); + sdfat_detach(inode); + dentry->d_time = dir->i_version; +out: + __unlock_d_revalidate(dentry); + __unlock_super(sb); + TMSG("%s exited with err(%d)\n", __func__, err); + return err; +} + +static int __sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct inode *old_inode, *new_inode; + struct super_block *sb = old_dir->i_sb; + loff_t i_pos; + int err; + + __lock_super(sb); + + TMSG("%s entered\n", __func__); + + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + + SDFAT_I(old_inode)->fid.size = i_size_read(old_inode); + + __cancel_dfr_work(old_inode, 0, 1, __func__); + + err = fsapi_rename(old_dir, &(SDFAT_I(old_inode)->fid), new_dir, new_dentry); + if (err) + goto out; + + __lock_d_revalidate(old_dentry); + __lock_d_revalidate(new_dentry); + + new_dir->i_version++; + new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = current_time(new_dir); + if (IS_DIRSYNC(new_dir)) + (void) sdfat_sync_inode(new_dir); + else + mark_inode_dirty(new_dir); + + i_pos = sdfat_make_i_pos(&(SDFAT_I(old_inode)->fid)); + sdfat_detach(old_inode); + sdfat_attach(old_inode, i_pos); + if (IS_DIRSYNC(new_dir)) + (void) sdfat_sync_inode(old_inode); + else + mark_inode_dirty(old_inode); + + if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) { + drop_nlink(old_dir); + if (!new_inode) + inc_nlink(new_dir); + } + + old_dir->i_version++; + old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); + if (IS_DIRSYNC(old_dir)) + (void) sdfat_sync_inode(old_dir); + else + mark_inode_dirty(old_dir); + + if (new_inode) { + sdfat_detach(new_inode); + + /* skip drop_nlink if new_inode already has been dropped */ + if (new_inode->i_nlink) { + drop_nlink(new_inode); + if (S_ISDIR(new_inode->i_mode)) + drop_nlink(new_inode); + } else { + EMSG("%s : abnormal access to an inode dropped\n", + __func__); + WARN_ON(new_inode->i_nlink == 0); + } + new_inode->i_ctime = current_time(new_inode); +#if 0 + (void) sdfat_sync_inode(new_inode); +#endif + } + +out: + __unlock_d_revalidate(old_dentry); + __unlock_d_revalidate(new_dentry); + __unlock_super(sb); + TMSG("%s exited with err(%d)\n", __func__, err); + return err; +} + +static int sdfat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = i_size_read(inode), count = size - i_size_read(inode); + int err, err2; + + err = generic_cont_expand_simple(inode, size); + if (err) + return err; + + inode->i_ctime = inode->i_mtime = current_time(inode); + mark_inode_dirty(inode); + + if (!IS_SYNC(inode)) + return 0; + + err = filemap_fdatawrite_range(mapping, start, start + count - 1); + err2 = sync_mapping_buffers(mapping); + err = (err)?(err):(err2); + err2 = write_inode_now(inode, 1); + err = (err)?(err):(err2); + if (err) + return err; + + return filemap_fdatawait_range(mapping, start, start + count - 1); +} + +static int sdfat_allow_set_time(struct sdfat_sb_info *sbi, struct inode *inode) +{ + mode_t allow_utime = sbi->options.allow_utime; + + if (!uid_eq(current_fsuid(), inode->i_uid)) { + if (in_group_p(inode->i_gid)) + allow_utime >>= 3; + if (allow_utime & MAY_WRITE) + return 1; + } + + /* use a default check */ + return 0; +} + +static int sdfat_sanitize_mode(const struct sdfat_sb_info *sbi, + struct inode *inode, umode_t *mode_ptr) +{ + mode_t i_mode, mask, perm; + + i_mode = inode->i_mode; + + if (S_ISREG(i_mode) || S_ISLNK(i_mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + perm = *mode_ptr & ~(S_IFMT | mask); + + /* Of the r and x bits, all (subject to umask) must be present.*/ + if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO | S_IXUGO))) + return -EPERM; + + if (sdfat_mode_can_hold_ro(inode)) { + /* Of the w bits, either all (subject to umask) or none must be present. */ + if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) + return -EPERM; + } else { + /* If sdfat_mode_can_hold_ro(inode) is false, can't change w bits. */ + if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) + return -EPERM; + } + + *mode_ptr &= S_IFMT | perm; + + return 0; +} + +static int sdfat_setattr(struct dentry *dentry, struct iattr *attr) +{ + + struct sdfat_sb_info *sbi = SDFAT_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + unsigned int ia_valid; + int error; + loff_t old_size; + + TMSG("%s entered\n", __func__); + + if ((attr->ia_valid & ATTR_SIZE) + && (attr->ia_size > i_size_read(inode))) { + error = sdfat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + return error; + attr->ia_valid &= ~ATTR_SIZE; + } + + /* Check for setting the inode time. */ + ia_valid = attr->ia_valid; + if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) + && sdfat_allow_set_time(sbi, inode)) { + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); + } + + error = setattr_prepare(dentry, attr); + attr->ia_valid = ia_valid; + if (error) + return error; + + if (((attr->ia_valid & ATTR_UID) && + (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || + ((attr->ia_valid & ATTR_GID) && + (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) || + ((attr->ia_valid & ATTR_MODE) && + (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { + return -EPERM; + } + + /* + * We don't return -EPERM here. Yes, strange, but this is too + * old behavior. + */ + if (attr->ia_valid & ATTR_MODE) { + if (sdfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) + attr->ia_valid &= ~ATTR_MODE; + } + + SDFAT_I(inode)->fid.size = i_size_read(inode); + + /* patch 1.2.0 : fixed the problem of size mismatch. */ + if (attr->ia_valid & ATTR_SIZE) { + old_size = i_size_read(inode); + + /* TO CHECK evicting directory works correctly */ + MMSG("%s: inode(%p) truncate size (%llu->%llu)\n", __func__, + inode, (u64)old_size, (u64)attr->ia_size); + __sdfat_do_truncate(inode, old_size, attr->ia_size); + } + setattr_copy(inode, attr); + mark_inode_dirty(inode); + + + TMSG("%s exited with err(%d)\n", __func__, error); + return error; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +static int sdfat_getattr(const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) +{ + struct inode *inode = path->dentry->d_inode; +#else +static int sdfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; +#endif + + TMSG("%s entered\n", __func__); + + generic_fillattr(inode, stat); + stat->blksize = SDFAT_SB(inode->i_sb)->fsi.cluster_size; + + TMSG("%s exited\n", __func__); + return 0; +} + +static const struct inode_operations sdfat_dir_inode_operations = { + .create = sdfat_create, + .lookup = sdfat_lookup, + .unlink = sdfat_unlink, + .symlink = sdfat_symlink, + .mkdir = sdfat_mkdir, + .rmdir = sdfat_rmdir, + .rename = sdfat_rename, + .setattr = sdfat_setattr, + .getattr = sdfat_getattr, +#ifdef CONFIG_SDFAT_VIRTUAL_XATTR + .listxattr = sdfat_listxattr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + .setxattr = sdfat_setxattr, + .getxattr = sdfat_getxattr, + .removexattr = sdfat_removexattr, +#endif +#endif +}; + +/*======================================================================*/ +/* File Operations */ +/*======================================================================*/ +static const struct inode_operations sdfat_symlink_inode_operations = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) + .readlink = generic_readlink, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + .get_link = sdfat_follow_link, +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) */ + .follow_link = sdfat_follow_link, +#endif +#ifdef CONFIG_SDFAT_VIRTUAL_XATTR + .listxattr = sdfat_listxattr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + .setxattr = sdfat_setxattr, + .getxattr = sdfat_getxattr, + .removexattr = sdfat_removexattr, +#endif +#endif +}; + +static int sdfat_file_release(struct inode *inode, struct file *filp) +{ + struct super_block *sb = inode->i_sb; + + /* Moved below code from sdfat_write_inode + * TO FIX size-mismatch problem. + */ + /* FIXME : Added bug_on to confirm that there is no size mismatch */ + sdfat_debug_bug_on(SDFAT_I(inode)->fid.size != i_size_read(inode)); + SDFAT_I(inode)->fid.size = i_size_read(inode); + fsapi_sync_fs(sb, 0); + return 0; +} + +static const struct file_operations sdfat_file_operations = { + .llseek = generic_file_llseek, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + .read = new_sync_read, + .write = new_sync_write, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) */ + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, +#endif + .mmap = sdfat_file_mmap, + .release = sdfat_file_release, + .unlocked_ioctl = sdfat_generic_ioctl, + .fsync = sdfat_file_fsync, + .splice_read = generic_file_splice_read, +}; + +static const struct address_space_operations sdfat_da_aops; +static const struct address_space_operations sdfat_aops; + +static void sdfat_truncate(struct inode *inode, loff_t old_size) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + unsigned int blocksize = 1 << inode->i_blkbits; + loff_t aligned_size; + int err; + + __lock_super(sb); + + if (SDFAT_I(inode)->fid.start_clu == 0) { + /* Stange statement: + * Empty start_clu != ~0 (not allocated) + */ + sdfat_fs_error(sb, "tried to truncate zeroed cluster."); + goto out; + } + + sdfat_debug_check_clusters(inode); + + __cancel_dfr_work(inode, (loff_t)i_size_read(inode), (loff_t)old_size, __func__); + + err = fsapi_truncate(inode, old_size, i_size_read(inode)); + if (err) + goto out; + + inode->i_ctime = inode->i_mtime = current_time(inode); + if (IS_DIRSYNC(inode)) + (void) sdfat_sync_inode(inode); + else + mark_inode_dirty(inode); + + // FIXME: 확인 요망 + // inode->i_blocks = ((SDFAT_I(inode)->i_size_ondisk + (fsi->cluster_size - 1)) + inode->i_blocks = ((i_size_read(inode) + (fsi->cluster_size - 1)) & + ~((loff_t)fsi->cluster_size - 1)) >> inode->i_blkbits; +out: + /* + * This protects against truncating a file bigger than it was then + * trying to write into the hole. + * + * comment by sh.hong: + * This seems to mean 'intra page/block' truncate and writing. + * I couldn't find a reason to change the values prior to fsapi_truncate + * Therefore, I switched the order of operations + * so that it's possible to utilize i_size_ondisk in fsapi_truncate + */ + + aligned_size = i_size_read(inode); + if (aligned_size & (blocksize - 1)) { + aligned_size |= (blocksize - 1); + aligned_size++; + } + + if (SDFAT_I(inode)->i_size_ondisk > i_size_read(inode)) + SDFAT_I(inode)->i_size_ondisk = aligned_size; + + sdfat_debug_check_clusters(inode); + + if (SDFAT_I(inode)->i_size_aligned > i_size_read(inode)) + SDFAT_I(inode)->i_size_aligned = aligned_size; + + /* After truncation : + * 1) Delayed allocation is OFF + * i_size = i_size_ondisk <= i_size_aligned + * (useless size var.) + * (block-aligned) + * 2) Delayed allocation is ON + * i_size = i_size_ondisk = i_size_aligned + * (will be block-aligned after write) + * or + * i_size_ondisk < i_size <= i_size_aligned (block_aligned) + * (will be block-aligned after write) + */ + + __unlock_super(sb); +} + +static const struct inode_operations sdfat_file_inode_operations = { + .setattr = sdfat_setattr, + .getattr = sdfat_getattr, +#ifdef CONFIG_SDFAT_VIRTUAL_XATTR + .listxattr = sdfat_listxattr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + .setxattr = sdfat_setxattr, + .getxattr = sdfat_getxattr, + .removexattr = sdfat_removexattr, +#endif +#endif +}; + +/*======================================================================*/ +/* Address Space Operations */ +/*======================================================================*/ +/* 2-level option flag */ +#define BMAP_NOT_CREATE 0 +#define BMAP_ADD_BLOCK 1 +#define BMAP_ADD_CLUSTER 2 +#define BLOCK_ADDED(bmap_ops) (bmap_ops) +static int sdfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks, int *create) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + const unsigned long blocksize = sb->s_blocksize; + const unsigned char blocksize_bits = sb->s_blocksize_bits; + sector_t last_block; + unsigned int cluster, clu_offset, sec_offset; + int err = 0; + + *phys = 0; + *mapped_blocks = 0; + + /* core code should handle EIO */ +#if 0 + if (fsi->prev_eio && BLOCK_ADDED(*create)) + return -EIO; +#endif + + if (((fsi->vol_type == FAT12) || (fsi->vol_type == FAT16)) && + (inode->i_ino == SDFAT_ROOT_INO)) { + if (sector < (fsi->dentries_in_root >> + (sb->s_blocksize_bits-DENTRY_SIZE_BITS))) { + *phys = sector + fsi->root_start_sector; + *mapped_blocks = 1; + } + return 0; + } + + last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; + if ((sector >= last_block) && (*create == BMAP_NOT_CREATE)) + return 0; + + /* Is this block already allocated? */ + clu_offset = sector >> fsi->sect_per_clus_bits; /* cluster offset */ + + SDFAT_I(inode)->fid.size = i_size_read(inode); + + + if (unlikely(__check_dfr_on(inode, + (loff_t)((loff_t)clu_offset << fsi->cluster_size_bits), + (loff_t)((loff_t)(clu_offset + 1) << fsi->cluster_size_bits), + __func__))) { + err = __do_dfr_map_cluster(inode, clu_offset, &cluster); + } else { + if (*create & BMAP_ADD_CLUSTER) + err = fsapi_map_clus(inode, clu_offset, &cluster, 1); + else + err = fsapi_map_clus(inode, clu_offset, &cluster, ALLOC_NOWHERE); + } + + if (err) { + if (err != -ENOSPC) + return -EIO; + return err; + } + + /* FOR BIGDATA */ + sdfat_statistics_set_rw(SDFAT_I(inode)->fid.flags, + clu_offset, *create & BMAP_ADD_CLUSTER); + + if (!IS_CLUS_EOF(cluster)) { + /* sector offset in cluster */ + sec_offset = sector & (fsi->sect_per_clus - 1); + + *phys = CLUS_TO_SECT(fsi, cluster) + sec_offset; + *mapped_blocks = fsi->sect_per_clus - sec_offset; + } +#if 0 + else { + /* Debug purpose (new clu needed) */ + ASSERT((*create & BMAP_ADD_CLUSTER) == 0); + ASSERT(sector >= last_block); + } +#endif + + if (sector < last_block) + *create = BMAP_NOT_CREATE; +#if 0 + else if (sector >= last_block) + *create = non-zero; + + if (iblock <= last mapped-block) + *phys != 0 + *create = BMAP_NOT_CREATE + else if (iblock <= last cluster) + *phys != 0 + *create = non-zero +#endif + return 0; +} + +static int sdfat_da_prep_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + unsigned long mapped_blocks; + sector_t phys; + loff_t pos; + int sec_offset; + int bmap_create = create ? BMAP_ADD_BLOCK : BMAP_NOT_CREATE; + int err = 0; + + __lock_super(sb); + + /* FAT32 only */ + ASSERT(fsi->vol_type == FAT32); + + err = sdfat_bmap(inode, iblock, &phys, &mapped_blocks, &bmap_create); + if (err) { + if (err != -ENOSPC) + sdfat_fs_error_ratelimit(sb, "%s: failed to bmap " + "(iblock:%u, err:%d)", __func__, + (u32)iblock, err); + goto unlock_ret; + } + + sec_offset = iblock & (fsi->sect_per_clus - 1); + + if (phys) { + /* the block in in the mapped cluster boundary */ + max_blocks = min(mapped_blocks, max_blocks); + map_bh(bh_result, sb, phys); + + BUG_ON(BLOCK_ADDED(bmap_create) && (sec_offset == 0)); + + } else if (create == 1) { + /* Not exist: new cluster needed */ + BUG_ON(!BLOCK_ADDED(bmap_create)); + + // Reserved Cluster (only if iblock is the first sector in a clu) + if (sec_offset == 0) { + err = fsapi_reserve_clus(inode); + if (err) { + if (err != -ENOSPC) + sdfat_fs_error_ratelimit(sb, + "%s: failed to bmap " + "(iblock:%u, err:%d)", __func__, + (u32)iblock, err); + + goto unlock_ret; + } + } + + // Delayed mapping + map_bh(bh_result, sb, ~((sector_t) 0xffff)); + set_buffer_new(bh_result); + set_buffer_delay(bh_result); + + } else { + /* get_block on non-existing addr. with create==0 */ + /* + * CHECKME: + * i_size_aligned 보다 작으면 delay 매핑을 일단 + * 켜줘야되는 게 아닌가? + * - 0-fill 을 항상 하기에, FAT 에서는 문제 없음. + * 중간에 영역이 꽉 찼으면, 디스크에 내려가지 않고는 + * invalidate 될 일이 없음 + */ + goto unlock_ret; + } + + + /* Newly added blocks */ + if (BLOCK_ADDED(bmap_create)) { + set_buffer_new(bh_result); + + SDFAT_I(inode)->i_size_aligned += max_blocks << sb->s_blocksize_bits; + if (phys) { + /* i_size_ondisk changes if a block added in the existing cluster */ + #define num_clusters(value) ((value) ? (s32)((value - 1) >> fsi->cluster_size_bits) + 1 : 0) + + /* FOR GRACEFUL ERROR HANDLING */ + if (num_clusters(SDFAT_I(inode)->i_size_aligned) != + num_clusters(SDFAT_I(inode)->i_size_ondisk)) { + EMSG("%s: inode(%p) invalid size (create(%d) " + "bmap_create(%d) phys(%lld) aligned(%lld) " + "on_disk(%lld) iblock(%u) sec_off(%d))\n", + __func__, inode, create, bmap_create, (s64)phys, + (s64)SDFAT_I(inode)->i_size_aligned, + (s64)SDFAT_I(inode)->i_size_ondisk, + (u32)iblock, + (s32)sec_offset); + sdfat_debug_bug_on(1); + } + SDFAT_I(inode)->i_size_ondisk = SDFAT_I(inode)->i_size_aligned; + } + + pos = (iblock + 1) << sb->s_blocksize_bits; + /* Debug purpose - defensive coding */ + ASSERT(SDFAT_I(inode)->i_size_aligned == pos); + if (SDFAT_I(inode)->i_size_aligned < pos) + SDFAT_I(inode)->i_size_aligned = pos; + /* Debug end */ + +#ifdef CONFIG_SDFAT_TRACE_IO + /* New page added (ASSERTION: 8 blocks per page) */ + if ((sec_offset & 7) == 0) + sbi->stat_n_pages_added++; +#endif + } + + /* FOR GRACEFUL ERROR HANDLING */ + if (i_size_read(inode) > SDFAT_I(inode)->i_size_aligned) { + sdfat_fs_error_ratelimit(sb, "%s: invalid size (inode(%p), " + "size(%llu) > aligned(%llu)\n", __func__, inode, + i_size_read(inode), SDFAT_I(inode)->i_size_aligned); + sdfat_debug_bug_on(1); + } + + bh_result->b_size = max_blocks << sb->s_blocksize_bits; + +unlock_ret: + __unlock_super(sb); + return err; +} + +static int sdfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + int err = 0; + unsigned long mapped_blocks; + sector_t phys; + loff_t pos; + int bmap_create = create ? BMAP_ADD_CLUSTER : BMAP_NOT_CREATE; + + __lock_super(sb); + err = sdfat_bmap(inode, iblock, &phys, &mapped_blocks, &bmap_create); + if (err) { + if (err != -ENOSPC) + sdfat_fs_error_ratelimit(sb, "%s: failed to bmap " + "(inode:%p iblock:%u, err:%d)", + __func__, inode, (u32)iblock, err); + goto unlock_ret; + } + + if (phys) { + max_blocks = min(mapped_blocks, max_blocks); + + /* Treat newly added block / cluster */ + if (BLOCK_ADDED(bmap_create) || buffer_delay(bh_result)) { + + /* Update i_size_ondisk */ + pos = (iblock + 1) << sb->s_blocksize_bits; + if (SDFAT_I(inode)->i_size_ondisk < pos) { + /* Debug purpose */ + if ((pos - SDFAT_I(inode)->i_size_ondisk) > bh_result->b_size) { + /* This never happens without DA */ + MMSG("Jumping get_block\n"); + } + + SDFAT_I(inode)->i_size_ondisk = pos; + sdfat_debug_check_clusters(inode); + } + + if (BLOCK_ADDED(bmap_create)) { + /* Old way (w/o DA) + * create == 1 only if iblock > i_size + * (in block unit) + */ + + /* 20130723 CHECK + * Truncate와 동시에 발생할 경우, + * i_size < (i_block 위치) 면서 buffer_delay()가 + * 켜져있을 수 있다. + * + * 기존에 할당된 영역을 다시 쓸 뿐이므로 큰 문제 + * 없지만, 그 경우, 미리 i_size_aligned 가 확장된 + * 영역이어야 한다. + */ + + /* FOR GRACEFUL ERROR HANDLING */ + if (buffer_delay(bh_result) && + (pos > SDFAT_I(inode)->i_size_aligned)) { + sdfat_fs_error(sb, "requested for bmap " + "out of range(pos:(%llu)>i_size_aligned(%llu)\n", + pos, SDFAT_I(inode)->i_size_aligned); + sdfat_debug_bug_on(1); + err = -EIO; + goto unlock_ret; + } + set_buffer_new(bh_result); + + /* + * adjust i_size_aligned if i_size_ondisk is + * bigger than it. (i.e. non-DA) + */ + if (SDFAT_I(inode)->i_size_ondisk > + SDFAT_I(inode)->i_size_aligned) { + SDFAT_I(inode)->i_size_aligned = + SDFAT_I(inode)->i_size_ondisk; + } + } + + if (buffer_delay(bh_result)) + clear_buffer_delay(bh_result); + +#if 0 + /* Debug purpose */ + if (SDFAT_I(inode)->i_size_ondisk > + SDFAT_I(inode)->i_size_aligned) { + /* Only after truncate + * and the two size variables should indicate + * same i_block + */ + unsigned int blocksize = 1 << inode->i_blkbits; + BUG_ON(SDFAT_I(inode)->i_size_ondisk - + SDFAT_I(inode)->i_size_aligned >= blocksize); + } +#endif + } + map_bh(bh_result, sb, phys); + } + + bh_result->b_size = max_blocks << sb->s_blocksize_bits; +unlock_ret: + __unlock_super(sb); + return err; +} + +static int sdfat_readpage(struct file *file, struct page *page) +{ + int ret; + + ret = mpage_readpage(page, sdfat_get_block); + return ret; +} + +static int sdfat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned int nr_pages) +{ + int ret; + + ret = mpage_readpages(mapping, pages, nr_pages, sdfat_get_block); + return ret; +} + +static inline void sdfat_submit_fullpage_bio(struct block_device *bdev, + sector_t sector, unsigned int length, struct page *page) +{ + /* Single page bio submit */ + struct bio *bio; + + BUG_ON((length > PAGE_SIZE) || (length == 0)); + + /* + * If __GFP_WAIT is set, then bio_alloc will always be able to allocate + * a bio. This is due to the mempool guarantees. To make this work, callers + * must never allocate more than 1 bio at a time from this pool. + * + * #define GFP_NOIO (__GFP_WAIT) + */ + bio = bio_alloc(GFP_NOIO, 1); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + bio_set_dev(bio, bdev); +#else + bio->bi_bdev = bdev; +#endif + bio->bi_vcnt = 1; + bio->bi_io_vec[0].bv_page = page; /* Inline vec */ + bio->bi_io_vec[0].bv_len = length; /* PAGE_SIZE */ + bio->bi_io_vec[0].bv_offset = 0; + __sdfat_set_bio_iterate(bio, sector, length, 0, 0); + + bio->bi_end_io = sdfat_writepage_end_io; + __sdfat_submit_bio_write(bio); +} + +static int sdfat_writepage(struct page *page, struct writeback_control *wbc) +{ + struct inode * const inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + loff_t i_size = i_size_read(inode); + const pgoff_t end_index = i_size >> PAGE_SHIFT; + const unsigned int blocks_per_page = PAGE_SIZE >> inode->i_blkbits; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + struct buffer_head *bh, *head; + sector_t block, block_0, last_phys; + int ret; + unsigned int nr_blocks_towrite = blocks_per_page; + + /* Don't distinguish 0-filled/clean block. + * Just write back the whole page + */ + if (fsi->cluster_size < PAGE_SIZE) + goto confused; + + if (!PageUptodate(page)) { + MMSG("%s: Not up-to-date page -> block_write_full_page\n", + __func__); + goto confused; + } + + if (page->index >= end_index) { + /* last page or outside i_size */ + unsigned int offset = i_size & (PAGE_SIZE-1); + + /* If a truncation is in progress */ + if (page->index > end_index || !offset) + goto confused; + + /* 0-fill after i_size */ + zero_user_segment(page, offset, PAGE_SIZE); + } + + if (!page_has_buffers(page)) { + MMSG("WP: No buffers -> block_write_full_page\n"); + goto confused; + } + + block = (sector_t)page->index << (PAGE_SHIFT - inode->i_blkbits); + block_0 = block; /* first block */ + head = page_buffers(page); + bh = head; + + last_phys = 0; + do { + BUG_ON(buffer_locked(bh)); + + if (!buffer_dirty(bh) || !buffer_uptodate(bh)) { + if (nr_blocks_towrite == blocks_per_page) + nr_blocks_towrite = (unsigned int) (block - block_0); + + BUG_ON(nr_blocks_towrite >= blocks_per_page); + + // !uptodate but dirty?? + if (buffer_dirty(bh)) + goto confused; + + // Nothing to writeback in this block + bh = bh->b_this_page; + block++; + continue; + } + + if (nr_blocks_towrite != blocks_per_page) + // Dirty -> Non-dirty -> Dirty again case + goto confused; + + /* Map if needed */ + if (!buffer_mapped(bh) || buffer_delay(bh)) { + BUG_ON(bh->b_size != (1 << (inode->i_blkbits))); + ret = sdfat_get_block(inode, block, bh, 1); + if (ret) + goto confused; + + if (buffer_new(bh)) { + clear_buffer_new(bh); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + clean_bdev_bh_alias(bh); +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) */ + unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr); +#endif + } + } + + /* continuity check */ + if (((last_phys + 1) != bh->b_blocknr) && (last_phys != 0)) { + DMSG("Non-contiguous block mapping in single page"); + goto confused; + } + + last_phys = bh->b_blocknr; + bh = bh->b_this_page; + block++; + } while (bh != head); + + if (nr_blocks_towrite == 0) { + DMSG("Page dirty but no dirty bh? alloc_208\n"); + goto confused; + } + + + /* Write-back */ + do { + clear_buffer_dirty(bh); + bh = bh->b_this_page; + } while (bh != head); + + BUG_ON(PageWriteback(page)); + set_page_writeback(page); + + /** + * Turn off MAPPED flag in victim's bh if defrag on. + * Another write_begin can starts after get_block for defrag victims called. + * In this case, write_begin calls get_block and get original block number + * and previous defrag will be canceled. + */ + if (unlikely(__check_dfr_on(inode, + (loff_t)(page->index << PAGE_SHIFT), + (loff_t)((page->index + 1) << PAGE_SHIFT), + __func__))) { + do { + clear_buffer_mapped(bh); + bh = bh->b_this_page; + } while (bh != head); + } + + // Trace # of pages queued (Approx.) + atomic_inc(&SDFAT_SB(sb)->stat_n_pages_queued); + + sdfat_submit_fullpage_bio(head->b_bdev, + head->b_blocknr << (inode->i_blkbits - sb->s_blocksize_bits), + nr_blocks_towrite << inode->i_blkbits, + page); + + unlock_page(page); + + return 0; + +confused: +#ifdef CONFIG_SDFAT_TRACE_IO + SDFAT_SB(sb)->stat_n_pages_confused++; +#endif + ret = block_write_full_page(page, sdfat_get_block, wbc); + return ret; +} + +static int sdfat_da_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + MMSG("%s(inode:%p) with nr_to_write = 0x%08lx " + "(ku %d, bg %d, tag %d, rc %d )\n", + __func__, mapping->host, wbc->nr_to_write, + wbc->for_kupdate, wbc->for_background, wbc->tagged_writepages, + wbc->for_reclaim); + + ASSERT(mapping->a_ops == &sdfat_da_aops); + +#ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE + if (SDFAT_SB(mapping->host->i_sb)->options.adj_req) + return sdfat_mpage_writepages(mapping, wbc, sdfat_get_block); +#endif + return generic_writepages(mapping, wbc); +} + +static int sdfat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + MMSG("%s(inode:%p) with nr_to_write = 0x%08lx " + "(ku %d, bg %d, tag %d, rc %d )\n", + __func__, mapping->host, wbc->nr_to_write, + wbc->for_kupdate, wbc->for_background, wbc->tagged_writepages, + wbc->for_reclaim); + + ASSERT(mapping->a_ops == &sdfat_aops); + +#ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE + if (SDFAT_SB(mapping->host->i_sb)->options.adj_req) + return sdfat_mpage_writepages(mapping, wbc, sdfat_get_block); +#endif + return mpage_writepages(mapping, wbc, sdfat_get_block); +} + +static void sdfat_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + + if (to > i_size_read(inode)) { + __sdfat_truncate_pagecache(inode, to, i_size_read(inode)); + sdfat_truncate(inode, SDFAT_I(inode)->i_size_aligned); + } +} + +static int __sdfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, + unsigned int flags, struct page **pagep, + void **fsdata, get_block_t *get_block, + loff_t *bytes, const char *fname) +{ + int ret; + + __cancel_dfr_work(mapping->host, pos, (loff_t)(pos + len), fname); + + *pagep = NULL; + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + get_block, bytes); + + if (ret < 0) + sdfat_write_failed(mapping, pos+len); + + return ret; +} + + +static int sdfat_da_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int flags, + struct page **pagep, void **fsdata) +{ + return __sdfat_write_begin(file, mapping, pos, len, flags, + pagep, fsdata, sdfat_da_prep_block, + &SDFAT_I(mapping->host)->i_size_aligned, + __func__); +} + + +static int sdfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int flags, + struct page **pagep, void **fsdata) +{ + return __sdfat_write_begin(file, mapping, pos, len, flags, + pagep, fsdata, sdfat_get_block, + &SDFAT_I(mapping->host)->i_size_ondisk, + __func__); +} + +static int sdfat_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int copied, + struct page *pagep, void *fsdata) +{ + struct inode *inode = mapping->host; + FILE_ID_T *fid = &(SDFAT_I(inode)->fid); + int err; + + err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); + + /* FOR GRACEFUL ERROR HANDLING */ + if (SDFAT_I(inode)->i_size_aligned < i_size_read(inode)) { + sdfat_fs_error(inode->i_sb, "invalid size(size(%llu) " + "> aligned(%llu)\n", i_size_read(inode), + SDFAT_I(inode)->i_size_aligned); + sdfat_debug_bug_on(1); + } + + if (err < len) + sdfat_write_failed(mapping, pos+len); + + if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { + inode->i_mtime = inode->i_ctime = current_time(inode); + fid->attr |= ATTR_ARCHIVE; + mark_inode_dirty(inode); + } + + return err; +} + +static inline ssize_t __sdfat_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, void *iov_u, loff_t offset, + loff_t count, unsigned long nr_segs) +{ + struct address_space *mapping = inode->i_mapping; + loff_t size = offset + count; + ssize_t ret; + + if (rw == WRITE) { + /* + * FIXME: blockdev_direct_IO() doesn't use ->write_begin(), + * so we need to update the ->i_size_aligned to block boundary. + * + * But we must fill the remaining area or hole by nul for + * updating ->i_size_aligned + * + * Return 0, and fallback to normal buffered write. + */ + if (SDFAT_I(inode)->i_size_aligned < size) + return 0; + } + + /* + * sdFAT need to use the DIO_LOCKING for avoiding the race + * condition of sdfat_get_block() and ->truncate(). + */ + ret = __sdfat_blkdev_direct_IO(rw, iocb, inode, iov_u, offset, nr_segs); + if (ret < 0 && (rw & WRITE)) + sdfat_write_failed(mapping, size); + + return ret; +} + +static const struct address_space_operations sdfat_aops = { + .readpage = sdfat_readpage, + .readpages = sdfat_readpages, + .writepage = sdfat_writepage, + .writepages = sdfat_writepages, + .write_begin = sdfat_write_begin, + .write_end = sdfat_write_end, + .direct_IO = sdfat_direct_IO, + .bmap = sdfat_aop_bmap +}; + +static const struct address_space_operations sdfat_da_aops = { + .readpage = sdfat_readpage, + .readpages = sdfat_readpages, + .writepage = sdfat_writepage, + .writepages = sdfat_da_writepages, + .write_begin = sdfat_da_write_begin, + .write_end = sdfat_write_end, + .direct_IO = sdfat_direct_IO, + .bmap = sdfat_aop_bmap +}; + +/*======================================================================*/ +/* Super Operations */ +/*======================================================================*/ + +static inline unsigned long sdfat_hash(loff_t i_pos) +{ + return hash_32(i_pos, SDFAT_HASH_BITS); +} + +static void sdfat_attach(struct inode *inode, loff_t i_pos) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(inode->i_sb); + struct hlist_head *head = sbi->inode_hashtable + sdfat_hash(i_pos); + + spin_lock(&sbi->inode_hash_lock); + SDFAT_I(inode)->i_pos = i_pos; + hlist_add_head(&SDFAT_I(inode)->i_hash_fat, head); + spin_unlock(&sbi->inode_hash_lock); +} + +static void sdfat_detach(struct inode *inode) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(inode->i_sb); + + spin_lock(&sbi->inode_hash_lock); + hlist_del_init(&SDFAT_I(inode)->i_hash_fat); + SDFAT_I(inode)->i_pos = 0; + spin_unlock(&sbi->inode_hash_lock); +} + + +/* doesn't deal with root inode */ +static int sdfat_fill_inode(struct inode *inode, const FILE_ID_T *fid) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(inode->i_sb); + FS_INFO_T *fsi = &(sbi->fsi); + DIR_ENTRY_T info; + u64 size = fid->size; + + memcpy(&(SDFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); + + SDFAT_I(inode)->i_pos = 0; + SDFAT_I(inode)->target = NULL; + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = get_seconds(); + + if (fsapi_read_inode(inode, &info) < 0) { + MMSG("%s: failed to read stat!\n", __func__); + return -EIO; + } + + if (info.Attr & ATTR_SUBDIR) { /* directory */ + inode->i_generation &= ~1; + inode->i_mode = sdfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &sdfat_dir_inode_operations; + inode->i_fop = &sdfat_dir_operations; + + set_nlink(inode, info.NumSubdirs); + } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */ + inode->i_op = &sdfat_symlink_inode_operations; + inode->i_generation |= 1; + inode->i_mode = sdfat_make_mode(sbi, info.Attr, S_IRWXUGO); + } else { /* regular file */ + inode->i_generation |= 1; + inode->i_mode = sdfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &sdfat_file_inode_operations; + inode->i_fop = &sdfat_file_operations; + + if (sbi->options.improved_allocation & SDFAT_ALLOC_DELAY) + inode->i_mapping->a_ops = &sdfat_da_aops; + else + inode->i_mapping->a_ops = &sdfat_aops; + + inode->i_mapping->nrpages = 0; + + } + + /* + * Use fid->size instead of info.Size + * because info.Size means the value saved on disk + */ + i_size_write(inode, size); + + /* ondisk and aligned size should be aligned with block size */ + if (size & (inode->i_sb->s_blocksize - 1)) { + size |= (inode->i_sb->s_blocksize - 1); + size++; + } + + SDFAT_I(inode)->i_size_aligned = size; + SDFAT_I(inode)->i_size_ondisk = size; + sdfat_debug_check_clusters(inode); + + sdfat_save_attr(inode, info.Attr); + + inode->i_blocks = ((i_size_read(inode) + (fsi->cluster_size - 1)) + & ~((loff_t)fsi->cluster_size - 1)) >> inode->i_blkbits; + + sdfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); + sdfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); + sdfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); + + __init_dfr_info(inode); + + return 0; +} + +static struct inode *sdfat_build_inode(struct super_block *sb, + const FILE_ID_T *fid, loff_t i_pos) { + struct inode *inode; + int err; + + inode = sdfat_iget(sb, i_pos); + if (inode) + goto out; + inode = new_inode(sb); + if (!inode) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + inode->i_ino = iunique(sb, SDFAT_ROOT_INO); + inode->i_version = 1; + err = sdfat_fill_inode(inode, fid); + if (err) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + sdfat_attach(inode, i_pos); + insert_inode_hash(inode); +out: + return inode; +} + +static struct inode *sdfat_alloc_inode(struct super_block *sb) +{ + struct sdfat_inode_info *ei; + + ei = kmem_cache_alloc(sdfat_inode_cachep, GFP_NOFS); + if (!ei) + return NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + init_rwsem(&ei->truncate_lock); +#endif + return &ei->vfs_inode; +} + +static void sdfat_destroy_inode(struct inode *inode) +{ + if (SDFAT_I(inode)->target) { + kfree(SDFAT_I(inode)->target); + SDFAT_I(inode)->target = NULL; + } + + kmem_cache_free(sdfat_inode_cachep, SDFAT_I(inode)); +} + +static int __sdfat_write_inode(struct inode *inode, int sync) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + DIR_ENTRY_T info; + + if (inode->i_ino == SDFAT_ROOT_INO) + return 0; + + info.Attr = sdfat_make_attr(inode); + info.Size = i_size_read(inode); + + sdfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); + sdfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); + sdfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); + + if (!__support_write_inode_sync(sb)) + sync = 0; + + /* FIXME : Do we need handling error? */ + return fsapi_write_inode(inode, &info, sync); +} + +static int sdfat_sync_inode(struct inode *inode) +{ + return __sdfat_write_inode(inode, 1); +} + +static int sdfat_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + return __sdfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); +} + +static void sdfat_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + + if (!inode->i_nlink) { + loff_t old_size = i_size_read(inode); + + i_size_write(inode, 0); + + SDFAT_I(inode)->fid.size = old_size; + + __cancel_dfr_work(inode, 0, (loff_t)old_size, __func__); + + /* TO CHECK evicting directory works correctly */ + MMSG("%s: inode(%p) evict %s (size(%llu) to zero)\n", + __func__, inode, + S_ISDIR(inode->i_mode) ? "directory" : "file", + (u64)old_size); + fsapi_truncate(inode, old_size, 0); + } + + invalidate_inode_buffers(inode); + clear_inode(inode); + fsapi_invalidate_extent(inode); + sdfat_detach(inode); + + /* after end of this function, caller will remove inode hash */ + /* remove_inode_hash(inode); */ +} + + + +static void sdfat_put_super(struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + int err; + + sdfat_log_msg(sb, KERN_INFO, "trying to unmount..."); + + __cancel_delayed_work_sync(sbi); + + if (__is_sb_dirty(sb)) + sdfat_write_super(sb); + + __free_dfr_mem_if_required(sb); + err = fsapi_umount(sb); + + if (sbi->nls_disk) { + unload_nls(sbi->nls_disk); + sbi->nls_disk = NULL; + sbi->options.codepage = sdfat_default_codepage; + } + if (sbi->nls_io) { + unload_nls(sbi->nls_io); + sbi->nls_io = NULL; + } + if (sbi->options.iocharset != sdfat_default_iocharset) { + kfree(sbi->options.iocharset); + sbi->options.iocharset = sdfat_default_iocharset; + } + + sb->s_fs_info = NULL; + + kobject_del(&sbi->sb_kobj); + kobject_put(&sbi->sb_kobj); + if (!sbi->use_vmalloc) + kfree(sbi); + else + vfree(sbi); + + sdfat_log_msg(sb, KERN_INFO, "unmounted successfully! %s", + err ? "(with previous I/O errors)" : ""); +} + +static inline void __flush_delayed_meta(struct super_block *sb, s32 sync) +{ +#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY + fsapi_cache_flush(sb, sync); +#else + /* DO NOTHING */ +#endif +} + +static void sdfat_write_super(struct super_block *sb) +{ + int time = 0; + + __lock_super(sb); + + __set_sb_clean(sb); + +#ifdef CONFIG_SDFAT_DFR + if (atomic_read(&(SDFAT_SB(sb)->dfr_info.stat)) == DFR_SB_STAT_VALID) + fsapi_dfr_update_fat_next(sb); +#endif + + /* flush delayed FAT/DIR dirty */ + __flush_delayed_meta(sb, 0); + + if (!(sb->s_flags & MS_RDONLY)) + fsapi_sync_fs(sb, 0); + + __unlock_super(sb); + + time = jiffies; + + /* Issuing bdev requests is needed + * to guarantee DIR updates in time + * whether w/ or w/o delayed DIR dirty feature. + * (otherwise DIR updates could be delayed for 5 + 5 secs at max.) + */ + sync_blockdev(sb->s_bdev); + +#if (defined(CONFIG_SDFAT_DFR) && defined(CONFIG_SDFAT_DFR_DEBUG)) + /* SPO test */ + fsapi_dfr_spo_test(sb, DFR_SPO_FAT_NEXT, __func__); +#endif + MMSG("BD: sdfat_write_super (bdev_sync for %ld ms)\n", + (jiffies - time) * 1000 / HZ); +} + + +static void __dfr_update_fat_next(struct super_block *sb) +{ +#ifdef CONFIG_SDFAT_DFR + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + + if (sbi->options.defrag && + (atomic_read(&sbi->dfr_info.stat) == DFR_SB_STAT_VALID)) { + fsapi_dfr_update_fat_next(sb); + } +#endif +} + +static void __dfr_update_fat_prev(struct super_block *sb, int wait) +{ +#ifdef CONFIG_SDFAT_DFR + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct defrag_info *sb_dfr = &sbi->dfr_info; + /* static time available? */ + static int time; /* initialized by zero */ + int uevent = 0, total = 0, clean = 0, full = 0; + int spent = jiffies - time; + + if (!(sbi->options.defrag && wait)) + return; + + __lock_super(sb); + /* Update FAT for defrag */ + if (atomic_read(&(sbi->dfr_info.stat)) == DFR_SB_STAT_VALID) { + + fsapi_dfr_update_fat_prev(sb, 0); + + /* flush delayed FAT/DIR dirty */ + __flush_delayed_meta(sb, 0); + + /* Complete defrag req */ + fsapi_sync_fs(sb, 1); + atomic_set(&sb_dfr->stat, DFR_SB_STAT_REQ); + complete_all(&sbi->dfr_complete); + } else if (((spent < 0) || (spent > DFR_DEFAULT_TIMEOUT)) && + (atomic_read(&(sbi->dfr_info.stat)) == DFR_SB_STAT_IDLE)) { + uevent = fsapi_dfr_check_dfr_required(sb, &total, &clean, &full); + time = jiffies; + } + __unlock_super(sb); + + if (uevent) { + kobject_uevent(&SDFAT_SB(sb)->sb_kobj, KOBJ_CHANGE); + dfr_debug("uevent for defrag_daemon, total_au %d, " + "clean_au %d, full_au %d", total, clean, full); + } +#endif +} + +static int sdfat_sync_fs(struct super_block *sb, int wait) +{ + int err = 0; + + /* If there are some dirty buffers in the bdev inode */ + if (__is_sb_dirty(sb)) { + __lock_super(sb); + __set_sb_clean(sb); + + __dfr_update_fat_next(sb); + + err = fsapi_sync_fs(sb, 1); + +#if (defined(CONFIG_SDFAT_DFR) && defined(CONFIG_SDFAT_DFR_DEBUG)) + /* SPO test */ + fsapi_dfr_spo_test(sb, DFR_SPO_FAT_NEXT, __func__); +#endif + + __unlock_super(sb); + } + + __dfr_update_fat_prev(sb, wait); + + return err; +} + +static int sdfat_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + /* + * patch 1.2.2 : + * fixed the slow-call problem because of volume-lock contention. + */ + struct super_block *sb = dentry->d_sb; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + VOL_INFO_T info; + + /* fsapi_statfs will try to get a volume lock if needed */ + if (fsapi_statfs(sb, &info)) + return -EIO; + + if (fsi->prev_eio) + sdfat_msg(sb, KERN_INFO, "called statfs with previous" + " I/O error(0x%02X).", fsi->prev_eio); + + buf->f_type = sb->s_magic; + buf->f_bsize = info.ClusterSize; + buf->f_blocks = info.NumClusters; + buf->f_bfree = info.FreeClusters; + buf->f_bavail = info.FreeClusters; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_namelen = 260; + + return 0; +} + +static int sdfat_remount(struct super_block *sb, int *flags, char *data) +{ + unsigned long prev_sb_flags; + char *orig_data = kstrdup(data, GFP_KERNEL); + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + + *flags |= MS_NODIRATIME; + + prev_sb_flags = sb->s_flags; + + sdfat_remount_syncfs(sb); + + fsapi_set_vol_flags(sb, VOL_CLEAN, 1); + + sdfat_log_msg(sb, KERN_INFO, "re-mounted(%s->%s), eio=0x%x, Opts: %s", + (prev_sb_flags & MS_RDONLY) ? "ro" : "rw", + (*flags & MS_RDONLY) ? "ro" : "rw", + fsi->prev_eio, orig_data); + kfree(orig_data); + return 0; +} + +static int __sdfat_show_options(struct seq_file *m, struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct sdfat_mount_options *opts = &sbi->options; + FS_INFO_T *fsi = &(sbi->fsi); + + /* Show partition info */ + seq_printf(m, ",fs=%s", sdfat_get_vol_type_str(fsi->vol_type)); + if (fsi->prev_eio) + seq_printf(m, ",eio=0x%x", fsi->prev_eio); + if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID)) + seq_printf(m, ",uid=%u", + from_kuid_munged(&init_user_ns, opts->fs_uid)); + if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID)) + seq_printf(m, ",gid=%u", + from_kgid_munged(&init_user_ns, opts->fs_gid)); + seq_printf(m, ",fmask=%04o", opts->fs_fmask); + seq_printf(m, ",dmask=%04o", opts->fs_dmask); + if (opts->allow_utime) + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); + if (sbi->nls_disk) + seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); + if (sbi->nls_io) + seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); + if (opts->utf8) + seq_puts(m, ",utf8"); + if (sbi->fsi.vol_type != EXFAT) + seq_puts(m, ",shortname=winnt"); + seq_printf(m, ",namecase=%u", opts->casesensitive); + if (opts->tz_utc) + seq_puts(m, ",tz=UTC"); + if (opts->improved_allocation & SDFAT_ALLOC_DELAY) + seq_puts(m, ",delay"); + if (opts->improved_allocation & SDFAT_ALLOC_SMART) + seq_printf(m, ",smart,ausize=%u", opts->amap_opt.sect_per_au); + if (opts->defrag) + seq_puts(m, ",defrag"); + if (opts->adj_hidsect) + seq_puts(m, ",adj_hid"); + if (opts->adj_req) + seq_puts(m, ",adj_req"); + seq_printf(m, ",symlink=%u", opts->symlink); + seq_printf(m, ",bps=%ld", sb->s_blocksize); + if (opts->errors == SDFAT_ERRORS_CONT) + seq_puts(m, ",errors=continue"); + else if (opts->errors == SDFAT_ERRORS_PANIC) + seq_puts(m, ",errors=panic"); + else + seq_puts(m, ",errors=remount-ro"); + if (opts->discard) + seq_puts(m, ",discard"); + + return 0; +} + +static const struct super_operations sdfat_sops = { + .alloc_inode = sdfat_alloc_inode, + .destroy_inode = sdfat_destroy_inode, + .write_inode = sdfat_write_inode, + .evict_inode = sdfat_evict_inode, + .put_super = sdfat_put_super, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + .write_super = sdfat_write_super, +#endif + .sync_fs = sdfat_sync_fs, + .statfs = sdfat_statfs, + .remount_fs = sdfat_remount, + .show_options = sdfat_show_options, +}; + +/*======================================================================*/ +/* SYSFS Operations */ +/*======================================================================*/ +#define SDFAT_ATTR(name, mode, show, store) \ +static struct sdfat_attr sdfat_attr_##name = __ATTR(name, mode, show, store) + +struct sdfat_attr { + struct attribute attr; + ssize_t (*show)(struct sdfat_sb_info *, char *); + ssize_t (*store)(struct sdfat_sb_info *, const char *, size_t); +}; + +static ssize_t sdfat_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct sdfat_sb_info *sbi = container_of(kobj, struct sdfat_sb_info, sb_kobj); + struct sdfat_attr *a = container_of(attr, struct sdfat_attr, attr); + + return a->show ? a->show(sbi, buf) : 0; +} + +static ssize_t sdfat_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct sdfat_sb_info *sbi = container_of(kobj, struct sdfat_sb_info, sb_kobj); + struct sdfat_attr *a = container_of(attr, struct sdfat_attr, attr); + + return a->store ? a->store(sbi, buf, len) : len; +} + +static const struct sysfs_ops sdfat_attr_ops = { + .show = sdfat_attr_show, + .store = sdfat_attr_store, +}; + + +static ssize_t type_show(struct sdfat_sb_info *sbi, char *buf) +{ + FS_INFO_T *fsi = &(sbi->fsi); + + return snprintf(buf, PAGE_SIZE, "%s\n", sdfat_get_vol_type_str(fsi->vol_type)); +} +SDFAT_ATTR(type, 0444, type_show, NULL); + +static ssize_t eio_show(struct sdfat_sb_info *sbi, char *buf) +{ + FS_INFO_T *fsi = &(sbi->fsi); + + return snprintf(buf, PAGE_SIZE, "0x%x\n", fsi->prev_eio); +} +SDFAT_ATTR(eio, 0444, eio_show, NULL); + +static ssize_t fratio_show(struct sdfat_sb_info *sbi, char *buf) +{ + unsigned int n_total_au = 0; + unsigned int n_clean_au = 0; + unsigned int n_full_au = 0; + unsigned int n_dirty_au = 0; + unsigned int fr = 0; + + n_total_au = fsapi_get_au_stat(sbi->host_sb, VOL_AU_STAT_TOTAL); + n_clean_au = fsapi_get_au_stat(sbi->host_sb, VOL_AU_STAT_CLEAN); + n_full_au = fsapi_get_au_stat(sbi->host_sb, VOL_AU_STAT_FULL); + n_dirty_au = n_total_au - (n_full_au + n_clean_au); + + if (!n_dirty_au) + fr = 0; + else if (!n_clean_au) + fr = 100; + else + fr = (n_dirty_au * 100) / (n_clean_au + n_dirty_au); + + return snprintf(buf, PAGE_SIZE, "%u\n", fr); +} +SDFAT_ATTR(fratio, 0444, fratio_show, NULL); + +static ssize_t totalau_show(struct sdfat_sb_info *sbi, char *buf) +{ + unsigned int n_au = 0; + + n_au = fsapi_get_au_stat(sbi->host_sb, VOL_AU_STAT_TOTAL); + return snprintf(buf, PAGE_SIZE, "%u\n", n_au); +} +SDFAT_ATTR(totalau, 0444, totalau_show, NULL); + +static ssize_t cleanau_show(struct sdfat_sb_info *sbi, char *buf) +{ + unsigned int n_clean_au = 0; + + n_clean_au = fsapi_get_au_stat(sbi->host_sb, VOL_AU_STAT_CLEAN); + return snprintf(buf, PAGE_SIZE, "%u\n", n_clean_au); +} +SDFAT_ATTR(cleanau, 0444, cleanau_show, NULL); + +static ssize_t fullau_show(struct sdfat_sb_info *sbi, char *buf) +{ + unsigned int n_full_au = 0; + + n_full_au = fsapi_get_au_stat(sbi->host_sb, VOL_AU_STAT_FULL); + return snprintf(buf, PAGE_SIZE, "%u\n", n_full_au); +} +SDFAT_ATTR(fullau, 0444, fullau_show, NULL); + +static struct attribute *sdfat_attrs[] = { + &sdfat_attr_type.attr, + &sdfat_attr_eio.attr, + &sdfat_attr_fratio.attr, + &sdfat_attr_totalau.attr, + &sdfat_attr_cleanau.attr, + &sdfat_attr_fullau.attr, + NULL, +}; + +static struct kobj_type sdfat_ktype = { + .default_attrs = sdfat_attrs, + .sysfs_ops = &sdfat_attr_ops, +}; + +static ssize_t version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "FS Version %s\n", SDFAT_VERSION); +} + +static struct kobj_attribute version_attr = __ATTR_RO(version); + +static struct attribute *attributes[] = { + &version_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = attributes, +}; + +/*======================================================================*/ +/* Super Block Read Operations */ +/*======================================================================*/ + +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_allow_utime, + Opt_codepage, + Opt_charset, + Opt_utf8, + Opt_namecase, + Opt_tz_utc, + Opt_adj_hidsect, + Opt_delay, + Opt_smart, + Opt_ausize, + Opt_packing, + Opt_defrag, + Opt_symlink, + Opt_debug, + Opt_err_cont, + Opt_err_panic, + Opt_err_ro, + Opt_err, + Opt_discard, + Opt_fs, + Opt_adj_req, +#ifdef CONFIG_SDFAT_USE_FOR_VFAT + Opt_shortname_lower, + Opt_shortname_win95, + Opt_shortname_winnt, + Opt_shortname_mixed, +#endif /* CONFIG_SDFAT_USE_FOR_VFAT */ +}; + +static const match_table_t sdfat_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_dmask, "dmask=%o"}, + {Opt_fmask, "fmask=%o"}, + {Opt_allow_utime, "allow_utime=%o"}, + {Opt_codepage, "codepage=%u"}, + {Opt_charset, "iocharset=%s"}, + {Opt_utf8, "utf8"}, + {Opt_namecase, "namecase=%u"}, + {Opt_tz_utc, "tz=UTC"}, + {Opt_adj_hidsect, "adj_hid"}, + {Opt_delay, "delay"}, + {Opt_smart, "smart"}, + {Opt_ausize, "ausize=%u"}, + {Opt_packing, "packing=%u"}, + {Opt_defrag, "defrag"}, + {Opt_symlink, "symlink=%u"}, + {Opt_debug, "debug"}, + {Opt_err_cont, "errors=continue"}, + {Opt_err_panic, "errors=panic"}, + {Opt_err_ro, "errors=remount-ro"}, + {Opt_discard, "discard"}, + {Opt_fs, "fs=%s"}, + {Opt_adj_req, "adj_req"}, +#ifdef CONFIG_SDFAT_USE_FOR_VFAT + {Opt_shortname_lower, "shortname=lower"}, + {Opt_shortname_win95, "shortname=win95"}, + {Opt_shortname_winnt, "shortname=winnt"}, + {Opt_shortname_mixed, "shortname=mixed"}, +#endif /* CONFIG_SDFAT_USE_FOR_VFAT */ + {Opt_err, NULL} +}; + +static int parse_options(struct super_block *sb, char *options, int silent, + int *debug, struct sdfat_mount_options *opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option, i; + char *tmpstr; + + opts->fs_uid = current_uid(); + opts->fs_gid = current_gid(); + opts->fs_fmask = opts->fs_dmask = current->fs->umask; + opts->allow_utime = (unsigned short) -1; + opts->codepage = sdfat_default_codepage; + opts->iocharset = sdfat_default_iocharset; + opts->casesensitive = 0; + opts->utf8 = 0; + opts->adj_hidsect = 0; + opts->tz_utc = 0; + opts->improved_allocation = 0; + opts->amap_opt.pack_ratio = 0; // Default packing + opts->amap_opt.sect_per_au = 0; + opts->amap_opt.misaligned_sect = 0; + opts->symlink = 0; + opts->errors = SDFAT_ERRORS_RO; + opts->discard = 0; + *debug = 0; + + if (!options) + goto out; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + + if (!*p) + continue; + token = match_token(p, sdfat_tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_uid = make_kuid(current_user_ns(), option); + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_gid = make_kgid(current_user_ns(), option); + break; + case Opt_umask: + case Opt_dmask: + case Opt_fmask: + if (match_octal(&args[0], &option)) + return 0; + if (token != Opt_dmask) + opts->fs_fmask = option; + if (token != Opt_fmask) + opts->fs_dmask = option; + break; + case Opt_allow_utime: + if (match_octal(&args[0], &option)) + return 0; + opts->allow_utime = option & (S_IWGRP | S_IWOTH); + break; + case Opt_codepage: + if (match_int(&args[0], &option)) + return 0; + opts->codepage = option; + break; + case Opt_charset: + if (opts->iocharset != sdfat_default_iocharset) + kfree(opts->iocharset); + tmpstr = match_strdup(&args[0]); + if (!tmpstr) + return -ENOMEM; + opts->iocharset = tmpstr; + break; + case Opt_namecase: + if (match_int(&args[0], &option)) + return 0; + opts->casesensitive = (option > 0) ? 1:0; + break; + case Opt_utf8: + opts->utf8 = 1; + break; + case Opt_adj_hidsect: + opts->adj_hidsect = 1; + break; + case Opt_tz_utc: + opts->tz_utc = 1; + break; + case Opt_symlink: + if (match_int(&args[0], &option)) + return 0; + opts->symlink = option > 0 ? 1 : 0; + break; + case Opt_delay: + opts->improved_allocation |= SDFAT_ALLOC_DELAY; + break; + case Opt_smart: + opts->improved_allocation |= SDFAT_ALLOC_SMART; + break; + case Opt_ausize: + if (match_int(&args[0], &option)) + return -EINVAL; + if (!is_power_of_2(option)) + return -EINVAL; + opts->amap_opt.sect_per_au = option; + IMSG("set AU size by option : %u sectors\n", option); + break; + case Opt_packing: + if (match_int(&args[0], &option)) + return 0; + opts->amap_opt.pack_ratio = option; + break; + case Opt_defrag: +#ifdef CONFIG_SDFAT_DFR + opts->defrag = 1; +#else + IMSG("defragmentation config is not enabled. ignore\n"); +#endif + break; + case Opt_err_cont: + opts->errors = SDFAT_ERRORS_CONT; + break; + case Opt_err_panic: + opts->errors = SDFAT_ERRORS_PANIC; + break; + case Opt_err_ro: + opts->errors = SDFAT_ERRORS_RO; + break; + case Opt_debug: + *debug = 1; + break; + case Opt_discard: + opts->discard = 1; + break; + case Opt_fs: + tmpstr = match_strdup(&args[0]); + if (!tmpstr) + return -ENOMEM; + for (i = 0; i < FS_TYPE_MAX; i++) { + if (!strcmp(tmpstr, FS_TYPE_STR[i])) { + opts->fs_type = (unsigned char)i; + sdfat_log_msg(sb, KERN_ERR, + "set fs-type by option : %s", + FS_TYPE_STR[i]); + break; + } + } + kfree(tmpstr); + if (i == FS_TYPE_MAX) { + sdfat_log_msg(sb, KERN_ERR, + "invalid fs-type, " + "only allow auto, exfat, vfat"); + return -EINVAL; + } + break; + case Opt_adj_req: +#ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE + opts->adj_req = 1; +#else + IMSG("adjust request config is not enabled. ignore\n"); +#endif + break; +#ifdef CONFIG_SDFAT_USE_FOR_VFAT + case Opt_shortname_lower: + case Opt_shortname_win95: + case Opt_shortname_mixed: + pr_warn("[SDFAT] DRAGONS AHEAD! sdFAT only understands \"shortname=winnt\"!\n"); + case Opt_shortname_winnt: + break; +#endif /* CONFIG_SDFAT_USE_FOR_VFAT */ + default: + if (!silent) { + sdfat_msg(sb, KERN_ERR, + "unrecognized mount option \"%s\" " + "or missing value", p); + } + return -EINVAL; + } + } + +out: + if (opts->allow_utime == (unsigned short) -1) + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); + + if (opts->utf8 && strcmp(opts->iocharset, sdfat_iocharset_with_utf8)) { + sdfat_msg(sb, KERN_WARNING, + "utf8 enabled, \"iocharset=%s\" is recommended", + sdfat_iocharset_with_utf8); + } + + if (opts->discard) { + struct request_queue *q = bdev_get_queue(sb->s_bdev); + + if (!blk_queue_discard(q)) + sdfat_msg(sb, KERN_WARNING, + "mounting with \"discard\" option, but " + "the device does not support discard"); + opts->discard = 0; + } + + return 0; +} + +static void sdfat_hash_init(struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + int i; + + spin_lock_init(&sbi->inode_hash_lock); + for (i = 0; i < SDFAT_HASH_SIZE; i++) + INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); +} + +static int sdfat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + FS_INFO_T *fsi = &(sbi->fsi); + DIR_ENTRY_T info; + + SDFAT_I(inode)->fid.dir.dir = fsi->root_dir; + SDFAT_I(inode)->fid.dir.flags = 0x01; + SDFAT_I(inode)->fid.entry = -1; + SDFAT_I(inode)->fid.start_clu = fsi->root_dir; + SDFAT_I(inode)->fid.flags = 0x01; + SDFAT_I(inode)->fid.type = TYPE_DIR; + SDFAT_I(inode)->fid.version = 0; + SDFAT_I(inode)->fid.rwoffset = 0; + SDFAT_I(inode)->fid.hint_bmap.off = CLUS_EOF; + SDFAT_I(inode)->fid.hint_stat.eidx = 0; + SDFAT_I(inode)->fid.hint_stat.clu = fsi->root_dir; + SDFAT_I(inode)->fid.hint_femp.eidx = -1; + + SDFAT_I(inode)->target = NULL; + + if (fsapi_read_inode(inode, &info) < 0) + return -EIO; + + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = 0; + inode->i_mode = sdfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); + inode->i_op = &sdfat_dir_inode_operations; + inode->i_fop = &sdfat_dir_operations; + + i_size_write(inode, info.Size); + SDFAT_I(inode)->fid.size = info.Size; + inode->i_blocks = ((i_size_read(inode) + (fsi->cluster_size - 1)) + & ~((loff_t)fsi->cluster_size - 1)) >> inode->i_blkbits; + SDFAT_I(inode)->i_pos = ((loff_t) fsi->root_dir << 32) | 0xffffffff; + SDFAT_I(inode)->i_size_aligned = i_size_read(inode); + SDFAT_I(inode)->i_size_ondisk = i_size_read(inode); + + sdfat_save_attr(inode, ATTR_SUBDIR); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + set_nlink(inode, info.NumSubdirs + 2); + return 0; +} + + + +static void setup_dops(struct super_block *sb) +{ + if (SDFAT_SB(sb)->options.casesensitive == 0) + sb->s_d_op = &sdfat_ci_dentry_ops; + else + sb->s_d_op = &sdfat_dentry_ops; +} + +static int sdfat_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode = NULL; + struct sdfat_sb_info *sbi; + int debug; + int err; + char buf[50]; + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + sdfat_log_msg(sb, KERN_INFO, "trying to mount..."); + + /* + * GFP_KERNEL is ok here, because while we do hold the + * supeblock lock, memory pressure can't call back into + * the filesystem, since we're only just about to mount + * it and have no inodes etc active! + */ + sbi = kzalloc(sizeof(struct sdfat_sb_info), GFP_KERNEL); + if (!sbi) { + sdfat_log_msg(sb, KERN_INFO, + "trying to alloc sbi with vzalloc()"); + sbi = vzalloc(sizeof(struct sdfat_sb_info)); + if (!sbi) { + sdfat_log_msg(sb, KERN_ERR, "failed to mount! (ENOMEM)"); + return -ENOMEM; + } + sbi->use_vmalloc = 1; + } + + mutex_init(&sbi->s_vlock); + sb->s_fs_info = sbi; + sb->s_flags |= MS_NODIRATIME; + sb->s_magic = SDFAT_SUPER_MAGIC; + sb->s_op = &sdfat_sops; + ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + err = parse_options(sb, data, silent, &debug, &sbi->options); + if (err) { + sdfat_log_msg(sb, KERN_ERR, "failed to parse options"); + goto failed_mount; + } + + setup_sdfat_xattr_handler(sb); + setup_sdfat_sync_super_wq(sb); + setup_dops(sb); + + err = fsapi_mount(sb); + if (err) { + sdfat_log_msg(sb, KERN_ERR, "failed to recognize fat type"); + goto failed_mount; + } + + /* set up enough so that it can read an inode */ + sdfat_hash_init(sb); + + /* + * The low byte of FAT's first entry must have same value with + * media-field. But in real world, too many devices is + * writing wrong value. So, removed that validity check. + * + * if (FAT_FIRST_ENT(sb, media) != first) + */ + + err = -EINVAL; + sprintf(buf, "cp%d", sbi->options.codepage); + sbi->nls_disk = load_nls(buf); + if (!sbi->nls_disk) { + sdfat_log_msg(sb, KERN_ERR, "codepage %s not found", buf); + goto failed_mount2; + } + + sbi->nls_io = load_nls(sbi->options.iocharset); + if (!sbi->nls_io) { + sdfat_log_msg(sb, KERN_ERR, "IO charset %s not found", + sbi->options.iocharset); + goto failed_mount2; + } + + err = __alloc_dfr_mem_if_required(sb); + if (err) { + sdfat_log_msg(sb, KERN_ERR, "failed to initialize a memory for " + "defragmentation"); + goto failed_mount3; + } + + err = -ENOMEM; + root_inode = new_inode(sb); + if (!root_inode) { + sdfat_log_msg(sb, KERN_ERR, "failed to allocate root inode."); + goto failed_mount3; + } + + root_inode->i_ino = SDFAT_ROOT_INO; + root_inode->i_version = 1; + + err = sdfat_read_root(root_inode); + if (err) { + sdfat_log_msg(sb, KERN_ERR, "failed to initialize root inode."); + goto failed_mount3; + } + + sdfat_attach(root_inode, SDFAT_I(root_inode)->i_pos); + insert_inode_hash(root_inode); + + err = -ENOMEM; + sb->s_root = __d_make_root(root_inode); + if (!sb->s_root) { + sdfat_msg(sb, KERN_ERR, "failed to get the root dentry"); + goto failed_mount3; + } + + /* + * Initialize filesystem attributes (for sysfs) + * ex: /sys/fs/sdfat/mmcblk1[179:17] + */ + sbi->sb_kobj.kset = sdfat_kset; + err = kobject_init_and_add(&sbi->sb_kobj, &sdfat_ktype, NULL, + "%s[%d:%d]", sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); + if (err) { + sdfat_msg(sb, KERN_ERR, "Unable to create sdfat attributes for" + " %s[%d:%d](%d)", sb->s_id, + MAJOR(bd_dev), MINOR(bd_dev), err); + goto failed_mount3; + } + + sdfat_log_msg(sb, KERN_INFO, "mounted successfully!"); + /* FOR BIGDATA */ + sdfat_statistics_set_mnt(&sbi->fsi); + sdfat_statistics_set_vol_size(sb); + return 0; + +failed_mount3: + __free_dfr_mem_if_required(sb); +failed_mount2: + fsapi_umount(sb); +failed_mount: + sdfat_log_msg(sb, KERN_INFO, "failed to mount! (%d)", err); + + if (root_inode) + iput(root_inode); + sb->s_root = NULL; + + if (sbi->nls_io) + unload_nls(sbi->nls_io); + if (sbi->nls_disk) + unload_nls(sbi->nls_disk); + if (sbi->options.iocharset != sdfat_default_iocharset) + kfree(sbi->options.iocharset); + sb->s_fs_info = NULL; + if (!sbi->use_vmalloc) + kfree(sbi); + else + vfree(sbi); + return err; +} + +static struct dentry *sdfat_fs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { + return mount_bdev(fs_type, flags, dev_name, data, sdfat_fill_super); +} + +static void init_once(void *foo) +{ + struct sdfat_inode_info *ei = (struct sdfat_inode_info *)foo; + + INIT_HLIST_NODE(&ei->i_hash_fat); + inode_init_once(&ei->vfs_inode); +} + +static int __init sdfat_init_inodecache(void) +{ + sdfat_inode_cachep = kmem_cache_create("sdfat_inode_cache", + sizeof(struct sdfat_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), + init_once); + if (!sdfat_inode_cachep) + return -ENOMEM; + return 0; +} + +static void sdfat_destroy_inodecache(void) +{ + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(sdfat_inode_cachep); +} + +#ifdef CONFIG_SDFAT_DBG_IOCTL +static void sdfat_debug_kill_sb(struct super_block *sb) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(sb); + struct block_device *bdev = sb->s_bdev; + + long flags; + + if (sbi) { + flags = sbi->debug_flags; + + if (flags & SDFAT_DEBUGFLAGS_INVALID_UMOUNT) { + /* invalidate_bdev drops all device cache include dirty. + * we use this to simulate device removal + */ + fsapi_cache_release(sb); + invalidate_bdev(bdev); + } + } + + kill_block_super(sb); +} +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + +static struct file_system_type sdfat_fs_type = { + .owner = THIS_MODULE, + .name = "sdfat", + .mount = sdfat_fs_mount, +#ifdef CONFIG_SDFAT_DBG_IOCTL + .kill_sb = sdfat_debug_kill_sb, +#else + .kill_sb = kill_block_super, +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + .fs_flags = FS_REQUIRES_DEV, +}; + +#ifdef CONFIG_SDFAT_USE_FOR_EXFAT +static struct file_system_type exfat_fs_type = { + .owner = THIS_MODULE, + .name = "exfat", + .mount = sdfat_fs_mount, +#ifdef CONFIG_SDFAT_DBG_IOCTL + .kill_sb = sdfat_debug_kill_sb, +#else + .kill_sb = kill_block_super, +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + .fs_flags = FS_REQUIRES_DEV, +}; +#endif /* CONFIG_SDFAT_USE_FOR_EXFAT */ + +#ifdef CONFIG_SDFAT_USE_FOR_VFAT +static struct file_system_type vfat_fs_type = { + .owner = THIS_MODULE, + .name = "vfat", + .mount = sdfat_fs_mount, +#ifdef CONFIG_SDFAT_DBG_IOCTL + .kill_sb = sdfat_debug_kill_sb, +#else + .kill_sb = kill_block_super, +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + .fs_flags = FS_REQUIRES_DEV, +}; +#endif /* CONFIG_SDFAT_USE_FOR_VFAT */ + +static int __init init_sdfat_fs(void) +{ + int err; + + sdfat_log_version(); + err = fsapi_init(); + if (err) + goto error; + + sdfat_kset = kset_create_and_add("sdfat", NULL, fs_kobj); + if (!sdfat_kset) { + pr_err("[SDFAT] failed to create fs_kobj\n"); + err = -ENOMEM; + goto error; + } + + err = sysfs_create_group(&sdfat_kset->kobj, &attr_group); + if (err) { + pr_err("[SDFAT] failed to create sdfat version attributes\n"); + goto error; + } + + err = sdfat_statistics_init(sdfat_kset); + if (err) + goto error; + + err = sdfat_init_inodecache(); + if (err) { + pr_err("[SDFAT] failed to initialize inode cache\n"); + goto error; + } + + err = register_filesystem(&sdfat_fs_type); + if (err) { + pr_err("[SDFAT] failed to register filesystem\n"); + goto error; + } + +#ifdef CONFIG_SDFAT_USE_FOR_EXFAT + err = register_filesystem(&exfat_fs_type); + if (err) { + pr_err("[SDFAT] failed to register for exfat filesystem\n"); + goto error; + } +#endif /* CONFIG_SDFAT_USE_FOR_EXFAT */ + +#ifdef CONFIG_SDFAT_USE_FOR_VFAT + err = register_filesystem(&vfat_fs_type); + if (err) { + pr_err("[SDFAT] failed to register for vfat filesystem\n"); + goto error; + } +#endif /* CONFIG_SDFAT_USE_FOR_VFAT */ + + return 0; +error: + sdfat_statistics_uninit(); + + if (sdfat_kset) { + sysfs_remove_group(&sdfat_kset->kobj, &attr_group); + kset_unregister(sdfat_kset); + sdfat_kset = NULL; + } + + sdfat_destroy_inodecache(); + fsapi_shutdown(); + + pr_err("[SDFAT] failed to initialize FS driver(err:%d)\n", err); + return err; +} + +static void __exit exit_sdfat_fs(void) +{ + sdfat_statistics_uninit(); + + if (sdfat_kset) { + sysfs_remove_group(&sdfat_kset->kobj, &attr_group); + kset_unregister(sdfat_kset); + sdfat_kset = NULL; + } + + sdfat_destroy_inodecache(); + unregister_filesystem(&sdfat_fs_type); +#ifdef CONFIG_SDFAT_USE_FOR_EXFAT + unregister_filesystem(&exfat_fs_type); +#endif /* CONFIG_SDFAT_USE_FOR_EXFAT */ +#ifdef CONFIG_SDFAT_USE_FOR_VFAT + unregister_filesystem(&vfat_fs_type); +#endif /* CONFIG_SDFAT_USE_FOR_VFAT */ + fsapi_shutdown(); +} + +module_init(init_sdfat_fs); +module_exit(exit_sdfat_fs); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("FAT/exFAT filesystem support"); +MODULE_AUTHOR("Samsung Electronics Co., Ltd."); diff --git a/fs/sdfat/sdfat.h b/fs/sdfat/sdfat.h new file mode 100644 index 0000000000000..6f764210fe4d7 --- /dev/null +++ b/fs/sdfat/sdfat.h @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _SDFAT_H +#define _SDFAT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "api.h" + +#ifdef CONFIG_SDFAT_DFR +#include "dfr.h" +#endif + +/* + * sdfat error flags + */ +#define SDFAT_ERRORS_CONT (1) /* ignore error and continue */ +#define SDFAT_ERRORS_PANIC (2) /* panic on error */ +#define SDFAT_ERRORS_RO (3) /* remount r/o on error */ + +/* + * sdfat allocator flags + */ +#define SDFAT_ALLOC_DELAY (1) /* Delayed allocation */ +#define SDFAT_ALLOC_SMART (2) /* Smart allocation */ + +/* + * sdfat allocator destination for smart allocation + */ +#define ALLOC_NOWHERE (0) +#define ALLOC_COLD (1) +#define ALLOC_HOT (16) +#define ALLOC_COLD_ALIGNED (1) +#define ALLOC_COLD_PACKING (2) +#define ALLOC_COLD_SEQ (4) + +/* + * sdfat nls lossy flag + */ +#define NLS_NAME_NO_LOSSY (0x00) /* no lossy */ +#define NLS_NAME_LOSSY (0x01) /* just detected incorrect filename(s) */ +#define NLS_NAME_OVERLEN (0x02) /* the length is over than its limit */ + +/* + * sdfat common MACRO + */ +#define CLUSTER_16(x) ((u16)((x) & 0xFFFFU)) +#define CLUSTER_32(x) ((u32)((x) & 0xFFFFFFFFU)) +#define CLUS_EOF CLUSTER_32(~0) +#define CLUS_BAD (0xFFFFFFF7U) +#define CLUS_FREE (0) +#define CLUS_BASE (2) +#define IS_CLUS_EOF(x) ((x) == CLUS_EOF) +#define IS_CLUS_BAD(x) ((x) == CLUS_BAD) +#define IS_CLUS_FREE(x) ((x) == CLUS_FREE) +#define IS_LAST_SECT_IN_CLUS(fsi, sec) \ + ((((sec) - (fsi)->data_start_sector + 1) \ + & ((1 << (fsi)->sect_per_clus_bits) - 1)) == 0) + +#define CLUS_TO_SECT(fsi, x) \ + ((((unsigned long long)(x) - CLUS_BASE) << (fsi)->sect_per_clus_bits) + (fsi)->data_start_sector) + +#define SECT_TO_CLUS(fsi, sec) \ + ((u32)((((sec) - (fsi)->data_start_sector) >> (fsi)->sect_per_clus_bits) + CLUS_BASE)) + +/* variables defined at sdfat.c */ +extern const char *FS_TYPE_STR[]; + +enum { + FS_TYPE_AUTO, + FS_TYPE_EXFAT, + FS_TYPE_VFAT, + FS_TYPE_MAX +}; + +/* + * sdfat mount in-memory data + */ +struct sdfat_mount_options { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + kuid_t fs_uid; + kgid_t fs_gid; +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */ + uid_t fs_uid; + gid_t fs_gid; +#endif + unsigned short fs_fmask; + unsigned short fs_dmask; + unsigned short allow_utime; /* permission for setting the [am]time */ + unsigned short codepage; /* codepage for shortname conversions */ + char *iocharset; /* charset for filename input/display */ + struct { + unsigned int pack_ratio; + unsigned int sect_per_au; + unsigned int misaligned_sect; + } amap_opt; /* AMAP-related options (see amap.c) */ + + unsigned char utf8; + unsigned char casesensitive; + unsigned char adj_hidsect; + unsigned char tz_utc; + unsigned char improved_allocation; + unsigned char defrag; + unsigned char symlink; /* support symlink operation */ + unsigned char errors; /* on error: continue, panic, remount-ro */ + unsigned char discard; /* flag on if -o dicard specified and device support discard() */ + unsigned char fs_type; /* fs_type that user specified */ + unsigned short adj_req; /* support aligned mpage write */ +}; + +#define SDFAT_HASH_BITS 8 +#define SDFAT_HASH_SIZE (1UL << SDFAT_HASH_BITS) + +/* + * SDFAT file system superblock in-memory data + */ +struct sdfat_sb_info { + FS_INFO_T fsi; /* private filesystem info */ + + struct mutex s_vlock; /* volume lock */ + int use_vmalloc; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + int s_dirt; + struct mutex s_lock; /* superblock lock */ + int write_super_queued; /* Write_super work is pending? */ + struct delayed_work write_super_work; /* Work_queue data structrue for write_super() */ + spinlock_t work_lock; /* Lock for WQ */ +#endif + struct super_block *host_sb; /* sb pointer */ + struct sdfat_mount_options options; + struct nls_table *nls_disk; /* Codepage used on disk */ + struct nls_table *nls_io; /* Charset used for input and display */ + struct ratelimit_state ratelimit; + + spinlock_t inode_hash_lock; + struct hlist_head inode_hashtable[SDFAT_HASH_SIZE]; + struct kobject sb_kobj; +#ifdef CONFIG_SDFAT_DBG_IOCTL + long debug_flags; +#endif /* CONFIG_SDFAT_DBG_IOCTL */ + +#ifdef CONFIG_SDFAT_DFR + struct defrag_info dfr_info; + struct completion dfr_complete; + unsigned int *dfr_new_clus; + int dfr_new_idx; + unsigned int *dfr_page_wb; + void **dfr_pagep; + unsigned int dfr_hint_clus; + unsigned int dfr_hint_idx; + int dfr_reserved_clus; + +#ifdef CONFIG_SDFAT_DFR_DEBUG + int dfr_spo_flag; +#endif /* CONFIG_SDFAT_DFR_DEBUG */ + +#endif /* CONFIG_SDFAT_DFR */ + +#ifdef CONFIG_SDFAT_TRACE_IO + /* Statistics for allocator */ + unsigned int stat_n_pages_written; /* # of written pages in total */ + unsigned int stat_n_pages_added; /* # of added blocks in total */ + unsigned int stat_n_bdev_pages_written; /* # of written pages owned by bdev inode */ + unsigned int stat_n_pages_confused; +#endif + atomic_t stat_n_pages_queued; /* # of pages in the request queue (approx.) */ +}; + +/* + * SDFAT file system inode in-memory data + */ +struct sdfat_inode_info { + FILE_ID_T fid; + char *target; + /* NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access */ + loff_t i_size_ondisk; /* physically allocated size */ + loff_t i_size_aligned; /* block-aligned i_size (used in cont_write_begin) */ + loff_t i_pos; /* on-disk position of directory entry or 0 */ + struct hlist_node i_hash_fat; /* hash by i_location */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + struct rw_semaphore truncate_lock; /* protect bmap against truncate */ +#endif +#ifdef CONFIG_SDFAT_DFR + struct defrag_info dfr_info; +#endif + struct inode vfs_inode; +}; + +/* + * FIXME : needs on-disk-slot in-memory data + */ + +/* static inline functons */ +static inline const char *sdfat_get_vol_type_str(unsigned int type) +{ + if (type == EXFAT) + return "exfat"; + else if (type == FAT32) + return "vfat:32"; + else if (type == FAT16) + return "vfat:16"; + else if (type == FAT12) + return "vfat:12"; + + return "unknown"; +} + +static inline struct sdfat_sb_info *SDFAT_SB(struct super_block *sb) +{ + return (struct sdfat_sb_info *)sb->s_fs_info; +} + +static inline struct sdfat_inode_info *SDFAT_I(struct inode *inode) +{ + return container_of(inode, struct sdfat_inode_info, vfs_inode); +} + +/* + * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to + * save ATTR_RO instead of ->i_mode. + * + * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only + * bit, it's just used as flag for app. + */ +static inline int sdfat_mode_can_hold_ro(struct inode *inode) +{ + struct sdfat_sb_info *sbi = SDFAT_SB(inode->i_sb); + + if (S_ISDIR(inode->i_mode)) + return 0; + + if ((~sbi->options.fs_fmask) & S_IWUGO) + return 1; + return 0; +} + +/* + * FIXME : needs to check symlink option. + */ +/* Convert attribute bits and a mask to the UNIX mode. */ +static inline mode_t sdfat_make_mode(struct sdfat_sb_info *sbi, + u32 attr, mode_t mode) +{ + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) + mode &= ~S_IWUGO; + + if (attr & ATTR_SUBDIR) + return (mode & ~sbi->options.fs_dmask) | S_IFDIR; + else if (attr & ATTR_SYMLINK) + return (mode & ~sbi->options.fs_dmask) | S_IFLNK; + else + return (mode & ~sbi->options.fs_fmask) | S_IFREG; +} + +/* Return the FAT attribute byte for this inode */ +static inline u32 sdfat_make_attr(struct inode *inode) +{ + u32 attrs = SDFAT_I(inode)->fid.attr; + + if (S_ISDIR(inode->i_mode)) + attrs |= ATTR_SUBDIR; + if (sdfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) + attrs |= ATTR_READONLY; + return attrs; +} + +static inline void sdfat_save_attr(struct inode *inode, u32 attr) +{ + if (sdfat_mode_can_hold_ro(inode)) + SDFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; + else + SDFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); +} + +/* sdfat/statistics.c */ +/* bigdata function */ +#ifdef CONFIG_SDFAT_STATISTICS +extern int sdfat_statistics_init(struct kset *sdfat_kset); +extern void sdfat_statistics_uninit(void); +extern void sdfat_statistics_set_mnt(FS_INFO_T *fsi); +extern void sdfat_statistics_set_mkdir(u8 flags); +extern void sdfat_statistics_set_create(u8 flags); +extern void sdfat_statistics_set_rw(u8 flags, u32 clu_offset, s32 create); +extern void sdfat_statistics_set_trunc(u8 flags, CHAIN_T *clu); +extern void sdfat_statistics_set_vol_size(struct super_block *sb); +#else +static inline int sdfat_statistics_init(struct kset *sdfat_kset) +{ + return 0; +} +static inline void sdfat_statistics_uninit(void) {}; +static inline void sdfat_statistics_set_mnt(FS_INFO_T *fsi) {}; +static inline void sdfat_statistics_set_mkdir(u8 flags) {}; +static inline void sdfat_statistics_set_create(u8 flags) {}; +static inline void sdfat_statistics_set_rw(u8 flags, u32 clu_offset, s32 create) {}; +static inline void sdfat_statistics_set_trunc(u8 flags, CHAIN_T *clu) {}; +static inline void sdfat_statistics_set_vol_size(struct super_block *sb) {}; +#endif + +/* sdfat/nls.c */ +/* NLS management function */ +s32 nls_cmp_sfn(struct super_block *sb, u8 *a, u8 *b); +s32 nls_cmp_uniname(struct super_block *sb, u16 *a, u16 *b); +s32 nls_uni16s_to_sfn(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname, s32 *p_lossy); +s32 nls_sfn_to_uni16s(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname); +s32 nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *uniname, u8 *p_cstring, s32 len); +s32 nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, + const s32 len, UNI_NAME_T *uniname, s32 *p_lossy); + +/* sdfat/mpage.c */ +#ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE +int sdfat_mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t *get_block); +#endif + +/* sdfat/xattr.c */ +#ifdef CONFIG_SDFAT_VIRTUAL_XATTR +void setup_sdfat_xattr_handler(struct super_block *sb); +extern int sdfat_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); +extern ssize_t sdfat_getxattr(struct dentry *dentry, const char *name, void *value, size_t size); +extern ssize_t sdfat_listxattr(struct dentry *dentry, char *list, size_t size); +extern int sdfat_removexattr(struct dentry *dentry, const char *name); +#else +static inline void setup_sdfat_xattr_handler(struct super_block *sb) {}; +#endif + +/* sdfat/misc.c */ +extern void +__sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) + __printf(3, 4) __cold; +#define sdfat_fs_error(sb, fmt, args...) \ + __sdfat_fs_error(sb, 1, fmt, ## args) +#define sdfat_fs_error_ratelimit(sb, fmt, args...) \ + __sdfat_fs_error(sb, __ratelimit(&SDFAT_SB(sb)->ratelimit), fmt, ## args) +extern void +__sdfat_msg(struct super_block *sb, const char *lv, int st, const char *fmt, ...) + __printf(4, 5) __cold; +#define sdfat_msg(sb, lv, fmt, args...) \ + __sdfat_msg(sb, lv, 0, fmt, ## args) +#define sdfat_log_msg(sb, lv, fmt, args...) \ + __sdfat_msg(sb, lv, 1, fmt, ## args) +extern void sdfat_log_version(void); +extern void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp); +extern void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp); +extern TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tm); + +#ifdef CONFIG_SDFAT_DEBUG + +#ifdef CONFIG_SDFAT_DBG_CAREFUL +void sdfat_debug_check_clusters(struct inode *inode); +#else +#define sdfat_debug_check_clusters(inode) +#endif /* CONFIG_SDFAT_DBG_CAREFUL */ + +#ifdef CONFIG_SDFAT_DBG_BUGON +#define sdfat_debug_bug_on(expr) BUG_ON(expr) +#else +#define sdfat_debug_bug_on(expr) +#endif + +#ifdef CONFIG_SDFAT_DBG_WARNON +#define sdfat_debug_warn_on(expr) WARN_ON(expr) +#else +#define sdfat_debug_warn_on(expr) +#endif + +#else /* CONFIG_SDFAT_DEBUG */ + +#define sdfat_debug_check_clusters(inode) +#define sdfat_debug_bug_on(expr) +#define sdfat_debug_warn_on(expr) + +#endif /* CONFIG_SDFAT_DEBUG */ + +#ifdef CONFIG_SDFAT_TRACE_ELAPSED_TIME +u32 sdfat_time_current_usec(struct timeval *tv); +extern struct timeval __t1; +extern struct timeval __t2; + +#define TIME_GET(tv) sdfat_time_current_usec(tv) +#define TIME_START(s) sdfat_time_current_usec(s) +#define TIME_END(e) sdfat_time_current_usec(e) +#define TIME_ELAPSED(s, e) ((u32)(((e)->tv_sec - (s)->tv_sec) * 1000000 + \ + ((e)->tv_usec - (s)->tv_usec))) +#define PRINT_TIME(n) pr_info("[SDFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1)) +#else /* CONFIG_SDFAT_TRACE_ELAPSED_TIME */ +#define TIME_GET(tv) (0) +#define TIME_START(s) +#define TIME_END(e) +#define TIME_ELAPSED(s, e) (0) +#define PRINT_TIME(n) +#endif /* CONFIG_SDFAT_TRACE_ELAPSED_TIME */ + +#define SDFAT_MSG_LV_NONE (0x00000000) +#define SDFAT_MSG_LV_ERR (0x00000001) +#define SDFAT_MSG_LV_INFO (0x00000002) +#define SDFAT_MSG_LV_DBG (0x00000003) +#define SDFAT_MSG_LV_MORE (0x00000004) +#define SDFAT_MSG_LV_TRACE (0x00000005) +#define SDFAT_MSG_LV_ALL (0x00000006) + +#define SDFAT_MSG_LEVEL SDFAT_MSG_LV_INFO + +#define SDFAT_TAG_NAME "SDFAT" +#define __S(x) #x +#define _S(x) __S(x) + +extern void __sdfat_dmsg(int level, const char *fmt, ...) __printf(2, 3) __cold; + +#define SDFAT_EMSG_T(level, ...) \ + __sdfat_dmsg(level, KERN_ERR "[" SDFAT_TAG_NAME "] [" _S(__FILE__) "(" _S(__LINE__) ")] " __VA_ARGS__) +#define SDFAT_DMSG_T(level, ...) \ + __sdfat_dmsg(level, KERN_INFO "[" SDFAT_TAG_NAME "] " __VA_ARGS__) + +#define SDFAT_EMSG(...) SDFAT_EMSG_T(SDFAT_MSG_LV_ERR, __VA_ARGS__) +#define SDFAT_IMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_INFO, __VA_ARGS__) +#define SDFAT_DMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_DBG, __VA_ARGS__) +#define SDFAT_MMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_MORE, __VA_ARGS__) +#define SDFAT_TMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_TRACE, __VA_ARGS__) + +#define EMSG(...) +#define IMSG(...) +#define DMSG(...) +#define MMSG(...) +#define TMSG(...) + +#define EMSG_VAR(exp) +#define IMSG_VAR(exp) +#define DMSG_VAR(exp) +#define MMSG_VAR(exp) +#define TMSG_VAR(exp) + +#ifdef CONFIG_SDFAT_DBG_MSG + + +#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_ERR) +#undef EMSG +#undef EMSG_VAR +#define EMSG(...) SDFAT_EMSG(__VA_ARGS__) +#define EMSG_VAR(exp) exp +#endif + +#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_INFO) +#undef IMSG +#undef IMSG_VAR +#define IMSG(...) SDFAT_IMSG(__VA_ARGS__) +#define IMSG_VAR(exp) exp +#endif + +#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_DBG) +#undef DMSG +#undef DMSG_VAR +#define DMSG(...) SDFAT_DMSG(__VA_ARGS__) +#define DMSG_VAR(exp) exp +#endif + +#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_MORE) +#undef MMSG +#undef MMSG_VAR +#define MMSG(...) SDFAT_MMSG(__VA_ARGS__) +#define MMSG_VAR(exp) exp +#endif + +/* should replace with trace function */ +#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_TRACE) +#undef TMSG +#undef TMSG_VAR +#define TMSG(...) SDFAT_TMSG(__VA_ARGS__) +#define TMSG_VAR(exp) exp +#endif + +#endif /* CONFIG_SDFAT_DBG_MSG */ + + +#define ASSERT(expr) { \ + if (!(expr)) { \ + pr_err("Assertion failed! %s\n", #expr); \ + BUG_ON(1); \ + } \ +} + +#endif /* !_SDFAT_H */ + diff --git a/fs/sdfat/sdfat_fs.h b/fs/sdfat/sdfat_fs.h new file mode 100644 index 0000000000000..998ca84c9a6ee --- /dev/null +++ b/fs/sdfat/sdfat_fs.h @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _SDFAT_FS_H +#define _SDFAT_FS_H + +#include +#include +#include + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ +#ifndef MSDOS_SUPER_MAGIC +#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */ +#endif + +#ifndef EXFAT_SUPER_MAGIC +#define EXFAT_SUPER_MAGIC (0x2011BAB0UL) +#endif /* EXFAT_SUPER_MAGIC */ + +#define SDFAT_SUPER_MAGIC (0x5EC5DFA4UL) +#define SDFAT_ROOT_INO 1 + +/* FAT types */ +#define FAT12 0x01 // FAT12 +#define FAT16 0x0E // Win95 FAT16 (LBA) +#define FAT32 0x0C // Win95 FAT32 (LBA) +#define EXFAT 0x07 // exFAT + +/* directory file name */ +#define DOS_CUR_DIR_NAME ". " +#define DOS_PAR_DIR_NAME ".. " + +#ifdef __LITTLE_ENDIAN +#define UNI_CUR_DIR_NAME ".\0" +#define UNI_PAR_DIR_NAME ".\0.\0" +#else +#define UNI_CUR_DIR_NAME "\0." +#define UNI_PAR_DIR_NAME "\0.\0." +#endif + +/* file name lengths */ +/* NOTE : + * The maximum length of input or output is limited to 256 including NULL, + * But we allocate 4 extra bytes for utf8 translation reside in last position, + * because utf8 can uses memory upto 6 bytes per one character. + * Therefore, MAX_CHARSET_SIZE supports upto 6 bytes for utf8 + */ +#define MAX_UNINAME_BUF_SIZE (((MAX_NAME_LENGTH+1)*2)+4) +#define MAX_DOSNAME_BUF_SIZE ((DOS_NAME_LENGTH+2)+6) +#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH+1)*MAX_CHARSET_SIZE) +#define MAX_CHARSET_SIZE 6 // max size of multi-byte character +#define MAX_NAME_LENGTH 255 // max len of file name excluding NULL +#define DOS_NAME_LENGTH 11 // DOS file name length excluding NULL + +#define DENTRY_SIZE 32 /* directory entry size */ +#define DENTRY_SIZE_BITS 5 + +#define MAX_FAT_DENTRIES 65536 /* FAT allows 65536 directory entries */ +#define MAX_EXFAT_DENTRIES 8388608 /* exFAT allows 8388608(256MB) directory entries */ + +/* PBR entries */ +#define PBR_SIGNATURE 0xAA55 +#define EXT_SIGNATURE 0xAA550000 +#define VOL_LABEL "NO NAME " /* size should be 11 */ +#define OEM_NAME "MSWIN4.1" /* size should be 8 */ +#define STR_FAT12 "FAT12 " /* size should be 8 */ +#define STR_FAT16 "FAT16 " /* size should be 8 */ +#define STR_FAT32 "FAT32 " /* size should be 8 */ +#define STR_EXFAT "EXFAT " /* size should be 8 */ + +#define VOL_CLEAN 0x0000 +#define VOL_DIRTY 0x0002 + +#define FAT_VOL_DIRTY 0x01 + +/* max number of clusters */ +#define FAT12_THRESHOLD 4087 // 2^12 - 1 + 2 (clu 0 & 1) +#define FAT16_THRESHOLD 65527 // 2^16 - 1 + 2 +#define FAT32_THRESHOLD 268435457 // 2^28 - 1 + 2 +#define EXFAT_THRESHOLD 268435457 // 2^28 - 1 + 2 + +/* dentry types */ +#define MSDOS_DELETED 0xE5 /* deleted mark */ +#define MSDOS_UNUSED 0x00 /* end of directory */ + +#define EXFAT_UNUSED 0x00 /* end of directory */ +#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */ +#define EXFAT_INVAL 0x80 /* invalid value */ +#define EXFAT_BITMAP 0x81 /* allocation bitmap */ +#define EXFAT_UPCASE 0x82 /* upcase table */ +#define EXFAT_VOLUME 0x83 /* volume label */ +#define EXFAT_FILE 0x85 /* file or dir */ +#define EXFAT_STREAM 0xC0 /* stream entry */ +#define EXFAT_NAME 0xC1 /* file name entry */ +#define EXFAT_ACL 0xC2 /* stream entry */ + +/* specific flag */ +#define MSDOS_LAST_LFN 0x40 + +/* file attributes */ +#define ATTR_NORMAL 0x0000 +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_SUBDIR 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_SYMLINK 0x0040 +#define ATTR_EXTEND (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | \ + ATTR_VOLUME) /* 0x000F */ + +#define ATTR_EXTEND_MASK (ATTR_EXTEND | ATTR_SUBDIR | ATTR_ARCHIVE) +#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \ + ATTR_SUBDIR | ATTR_ARCHIVE | ATTR_SYMLINK)/* 0x007E */ + +/* file creation modes */ +#define FM_REGULAR 0x00 +#define FM_SYMLINK 0x40 + +/* time modes */ +#define TM_CREATE 0 +#define TM_MODIFY 1 +#define TM_ACCESS 2 + +/* checksum types */ +#define CS_DIR_ENTRY 0 +#define CS_PBR_SECTOR 1 +#define CS_DEFAULT 2 + +/* + * ioctl command + */ +#define SDFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) +#define SDFAT_IOCTL_DFR_INFO _IOC(_IOC_NONE, 'E', 0x13, sizeof(u32)) +#define SDFAT_IOCTL_DFR_TRAV _IOC(_IOC_NONE, 'E', 0x14, sizeof(u32)) +#define SDFAT_IOCTL_DFR_REQ _IOC(_IOC_NONE, 'E', 0x15, sizeof(u32)) +#define SDFAT_IOCTL_DFR_SPO_FLAG _IOC(_IOC_NONE, 'E', 0x16, sizeof(u32)) +#define SDFAT_IOCTL_PANIC _IOC(_IOC_NONE, 'E', 0x17, sizeof(u32)) + +/* + * ioctl command for debugging + */ + +/* + * IOCTL code 'f' used by + * - file systems typically #0~0x1F + * - embedded terminal devices #128~ + * - exts for debugging purpose #99 + * number 100 and 101 is available now but has possible conflicts + * + * NOTE : This is available only If CONFIG_SDFAT_DVBG_IOCTL is enabled. + * + */ +#define SDFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long) +#define SDFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long) + +#define SDFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01 +#define SDFAT_DEBUGFLAGS_ERROR_RW 0x02 + +/*----------------------------------------------------------------------*/ +/* On-Disk Type Definitions */ +/*----------------------------------------------------------------------*/ + +/* FAT12/16 BIOS parameter block (64 bytes) */ +typedef struct { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + + __u8 sect_size[2]; /* unaligned */ + __u8 sect_per_clus; + __le16 num_reserved; /* . */ + __u8 num_fats; + __u8 num_root_entries[2]; /* unaligned */ + __u8 num_sectors[2]; /* unaligned */ + __u8 media_type; + __le16 num_fat_sectors; + __le16 sectors_in_track; + __le16 num_heads; + __le32 num_hid_sectors; /* . */ + __le32 num_huge_sectors; + + __u8 phy_drv_no; + __u8 state; /* used by WindowsNT for mount state */ + __u8 ext_signature; + __u8 vol_serial[4]; + __u8 vol_label[11]; + __u8 vol_type[8]; + __le16 dummy; +} bpb16_t; + +/* FAT32 BIOS parameter block (64 bytes) */ +typedef struct { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + + __u8 sect_size[2]; /* unaligned */ + __u8 sect_per_clus; + __le16 num_reserved; + __u8 num_fats; + __u8 num_root_entries[2]; /* unaligned */ + __u8 num_sectors[2]; /* unaligned */ + __u8 media_type; + __le16 num_fat_sectors; /* zero */ + __le16 sectors_in_track; + __le16 num_heads; + __le32 num_hid_sectors; /* . */ + __le32 num_huge_sectors; + + __le32 num_fat32_sectors; + __le16 ext_flags; + __u8 fs_version[2]; + __le32 root_cluster; /* . */ + __le16 fsinfo_sector; + __le16 backup_sector; + __le16 reserved[6]; /* . */ +} bpb32_t; + +/* FAT32 EXTEND BIOS parameter block (32 bytes) */ +typedef struct { + __u8 phy_drv_no; + __u8 state; /* used by WindowsNT for mount state */ + __u8 ext_signature; + __u8 vol_serial[4]; + __u8 vol_label[11]; + __u8 vol_type[8]; + __le16 dummy[3]; +} bsx32_t; + +/* EXFAT BIOS parameter block (64 bytes) */ +typedef struct { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + __u8 res_zero[53]; +} bpb64_t; + +/* EXFAT EXTEND BIOS parameter block (56 bytes) */ +typedef struct { + __le64 vol_offset; + __le64 vol_length; + __le32 fat_offset; + __le32 fat_length; + __le32 clu_offset; + __le32 clu_count; + __le32 root_cluster; + __le32 vol_serial; + __u8 fs_version[2]; + __le16 vol_flags; + __u8 sect_size_bits; + __u8 sect_per_clus_bits; + __u8 num_fats; + __u8 phy_drv_no; + __u8 perc_in_use; + __u8 reserved2[7]; +} bsx64_t; + +/* FAT32 PBR (64 bytes) */ +typedef struct { + bpb16_t bpb; +} pbr16_t; + +/* FAT32 PBR[BPB+BSX] (96 bytes) */ +typedef struct { + bpb32_t bpb; + bsx32_t bsx; +} pbr32_t; + +/* EXFAT PBR[BPB+BSX] (120 bytes) */ +typedef struct { + bpb64_t bpb; + bsx64_t bsx; +} pbr64_t; + +/* Common PBR[Partition Boot Record] (512 bytes) */ +typedef struct { + union { + __u8 raw[64]; + bpb16_t f16; + bpb32_t f32; + bpb64_t f64; + } bpb; + union { + __u8 raw[56]; + bsx32_t f32; + bsx64_t f64; + } bsx; + __u8 boot_code[390]; + __le16 signature; +} pbr_t; + +/* FAT32 filesystem information sector (512 bytes) */ +typedef struct { + __le32 signature1; // aligned + __u8 reserved1[480]; + __le32 signature2; // aligned + __le32 free_cluster; // aligned + __le32 next_cluster; // aligned + __u8 reserved2[14]; + __le16 signature3[2]; +} fat32_fsi_t; + +/* FAT directory entry (32 bytes) */ +typedef struct { + __u8 dummy[32]; +} DENTRY_T; + +typedef struct { + __u8 name[DOS_NAME_LENGTH]; /* 11 chars */ + __u8 attr; + __u8 lcase; + __u8 create_time_ms; + __le16 create_time; // aligned + __le16 create_date; // aligned + __le16 access_date; // aligned + __le16 start_clu_hi; // aligned + __le16 modify_time; // aligned + __le16 modify_date; // aligned + __le16 start_clu_lo; // aligned + __le32 size; // aligned +} DOS_DENTRY_T; + +/* FAT extended directory entry (32 bytes) */ +typedef struct { + __u8 order; + __u8 unicode_0_4[10]; + __u8 attr; + __u8 sysid; + __u8 checksum; + __le16 unicode_5_10[6]; // aligned + __le16 start_clu; // aligned + __le16 unicode_11_12[2]; // aligned +} EXT_DENTRY_T; + +/* EXFAT file directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 num_ext; + __le16 checksum; // aligned + __le16 attr; // aligned + __le16 reserved1; + __le16 create_time; // aligned + __le16 create_date; // aligned + __le16 modify_time; // aligned + __le16 modify_date; // aligned + __le16 access_time; // aligned + __le16 access_date; // aligned + __u8 create_time_ms; + __u8 modify_time_ms; + __u8 access_time_ms; + __u8 reserved2[9]; +} FILE_DENTRY_T; + +/* EXFAT stream extension directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 flags; + __u8 reserved1; + __u8 name_len; + __le16 name_hash; // aligned + __le16 reserved2; + __le64 valid_size; // aligned + __le32 reserved3; // aligned + __le32 start_clu; // aligned + __le64 size; // aligned +} STRM_DENTRY_T; + +/* EXFAT file name directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 flags; + __le16 unicode_0_14[15]; // aligned +} NAME_DENTRY_T; + +/* EXFAT allocation bitmap directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 flags; + __u8 reserved[18]; + __le32 start_clu; // aligned + __le64 size; // aligned +} BMAP_DENTRY_T; + +/* EXFAT up-case table directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 reserved1[3]; + __le32 checksum; // aligned + __u8 reserved2[12]; + __le32 start_clu; // aligned + __le64 size; // aligned +} CASE_DENTRY_T; + +/* EXFAT volume label directory entry (32 bytes) */ +typedef struct { + __u8 type; + __u8 label_len; + __le16 unicode_0_10[11]; // aligned + __u8 reserved[8]; +} VOLM_DENTRY_T; + +#endif /* _SDFAT_FS_H */ diff --git a/fs/sdfat/statistics.c b/fs/sdfat/statistics.c new file mode 100644 index 0000000000000..00624df5a6364 --- /dev/null +++ b/fs/sdfat/statistics.c @@ -0,0 +1,262 @@ +#include "sdfat.h" + +#define SDFAT_VF_CLUS_MAX 7 /* 512 Byte ~ 32 KByte */ +#define SDFAT_EF_CLUS_MAX 17 /* 512 Byte ~ 32 MByte */ + +enum { + SDFAT_MNT_FAT12, + SDFAT_MNT_FAT16, + SDFAT_MNT_FAT32, + SDFAT_MNT_EXFAT, + SDFAT_MNT_MAX +}; + +enum { + SDFAT_OP_EXFAT_MNT, + SDFAT_OP_MKDIR, + SDFAT_OP_CREATE, + SDFAT_OP_READ, + SDFAT_OP_WRITE, + SDFAT_OP_TRUNC, + SDFAT_OP_MAX +}; + +enum { + SDFAT_VOL_4G, + SDFAT_VOL_8G, + SDFAT_VOL_16G, + SDFAT_VOL_32G, + SDFAT_VOL_64G, + SDFAT_VOL_128G, + SDFAT_VOL_256G, + SDFAT_VOL_512G, + SDFAT_VOL_XTB, + SDFAT_VOL_MAX +}; + +static struct sdfat_statistics { + u32 clus_vfat[SDFAT_VF_CLUS_MAX]; + u32 clus_exfat[SDFAT_EF_CLUS_MAX]; + u32 mnt_cnt[SDFAT_MNT_MAX]; + u32 nofat_op[SDFAT_OP_MAX]; + u32 vol_size[SDFAT_VOL_MAX]; +} statistics; + +static struct kset *sdfat_statistics_kset; + +static ssize_t vfat_cl_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "VCL_512B_I:%u,VCL_1K_I:%u,VCL_2K_I:%u," + "VCL_4K_I:%u,VCL_8K_I:%u,VCL_16K_I:%u,VCL_32K_I:%u\n", + statistics.clus_vfat[0], statistics.clus_vfat[1], + statistics.clus_vfat[2], statistics.clus_vfat[3], + statistics.clus_vfat[4], statistics.clus_vfat[5], + statistics.clus_vfat[6]); +} + +static ssize_t exfat_cl_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "ECL_512B_I:%u,ECL_1K_I:%u,ECL_2K_I:%u," + "ECL_4K_I:%u,ECL_8K_I:%u,ECL_16K_I:%u,ECL_32K_I:%u,ECL_64K_I:%u," + "ECL_128K_I:%u,ECL_256K_I:%u,ECL_512K_I:%u,ECL_1M_I:%u," + "ECL_2M_I:%u,ECL_4M_I:%u,ECL_8M_I:%u,ECL_16M_I:%u,ECL_32M_I:%u\n", + statistics.clus_exfat[0], statistics.clus_exfat[1], + statistics.clus_exfat[2], statistics.clus_exfat[3], + statistics.clus_exfat[4], statistics.clus_exfat[5], + statistics.clus_exfat[6], statistics.clus_exfat[7], + statistics.clus_exfat[8], statistics.clus_exfat[9], + statistics.clus_exfat[10], statistics.clus_exfat[11], + statistics.clus_exfat[12], statistics.clus_exfat[13], + statistics.clus_exfat[14], statistics.clus_exfat[15], + statistics.clus_exfat[16]); +} + +static ssize_t mount_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "FAT12_MNT_I:%u,FAT16_MNT_I:%u,FAT32_MNT_I:%u," + "EXFAT_MNT_I:%u\n", + statistics.mnt_cnt[SDFAT_MNT_FAT12], + statistics.mnt_cnt[SDFAT_MNT_FAT16], + statistics.mnt_cnt[SDFAT_MNT_FAT32], + statistics.mnt_cnt[SDFAT_MNT_EXFAT]); +} + +static ssize_t nofat_op_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "NOFAT_MOUNT_I:%u,NOFAT_MKDIR_I:%u,NOFAT_CREATE_I:%u," + "NOFAT_READ_I:%u,NOFAT_WRITE_I:%u,NOFAT_TRUNC_I:%u\n", + statistics.nofat_op[SDFAT_OP_EXFAT_MNT], + statistics.nofat_op[SDFAT_OP_MKDIR], + statistics.nofat_op[SDFAT_OP_CREATE], + statistics.nofat_op[SDFAT_OP_READ], + statistics.nofat_op[SDFAT_OP_WRITE], + statistics.nofat_op[SDFAT_OP_TRUNC]); +} + +static ssize_t vol_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "VOL_4G_I:%u,VOL_8G_I:%u,VOL_16G_I:%u," + "VOL_32G_I:%u,VOL_64G_I:%u,VOL_128G_I:%u,VOL_256G_I:%u," + "VOL_512G_I:%u,VOL_XTB_I:%u\n", + statistics.vol_size[SDFAT_VOL_4G], + statistics.vol_size[SDFAT_VOL_8G], + statistics.vol_size[SDFAT_VOL_16G], + statistics.vol_size[SDFAT_VOL_32G], + statistics.vol_size[SDFAT_VOL_64G], + statistics.vol_size[SDFAT_VOL_128G], + statistics.vol_size[SDFAT_VOL_256G], + statistics.vol_size[SDFAT_VOL_512G], + statistics.vol_size[SDFAT_VOL_XTB]); +} + +static struct kobj_attribute vfat_cl_attr = __ATTR_RO(vfat_cl); +static struct kobj_attribute exfat_cl_attr = __ATTR_RO(exfat_cl); +static struct kobj_attribute mount_attr = __ATTR_RO(mount); +static struct kobj_attribute nofat_op_attr = __ATTR_RO(nofat_op); +static struct kobj_attribute vol_size_attr = __ATTR_RO(vol_size); + +static struct attribute *attributes_statistics[] = { + &vfat_cl_attr.attr, + &exfat_cl_attr.attr, + &mount_attr.attr, + &nofat_op_attr.attr, + &vol_size_attr.attr, + NULL, +}; + +static struct attribute_group attr_group_statistics = { + .attrs = attributes_statistics, +}; + +int sdfat_statistics_init(struct kset *sdfat_kset) +{ + int err; + + sdfat_statistics_kset = kset_create_and_add("statistics", NULL, &sdfat_kset->kobj); + if (!sdfat_statistics_kset) { + pr_err("[SDFAT] failed to create sdfat statistics kobj\n"); + return -ENOMEM; + } + + err = sysfs_create_group(&sdfat_statistics_kset->kobj, &attr_group_statistics); + if (err) { + pr_err("[SDFAT] failed to create sdfat statistics attributes\n"); + kset_unregister(sdfat_statistics_kset); + sdfat_statistics_kset = NULL; + return err; + } + + return 0; +} + +void sdfat_statistics_uninit(void) +{ + if (sdfat_statistics_kset) { + sysfs_remove_group(&sdfat_statistics_kset->kobj, &attr_group_statistics); + kset_unregister(sdfat_statistics_kset); + sdfat_statistics_kset = NULL; + } + memset(&statistics, 0, sizeof(struct sdfat_statistics)); +} + +void sdfat_statistics_set_mnt(FS_INFO_T *fsi) +{ + if (fsi->vol_type == EXFAT) { + statistics.mnt_cnt[SDFAT_MNT_EXFAT]++; + statistics.nofat_op[SDFAT_OP_EXFAT_MNT] = 1; + if (fsi->sect_per_clus_bits < SDFAT_EF_CLUS_MAX) + statistics.clus_exfat[fsi->sect_per_clus_bits]++; + else + statistics.clus_exfat[SDFAT_EF_CLUS_MAX - 1]++; + return; + } + + if (fsi->vol_type == FAT32) + statistics.mnt_cnt[SDFAT_MNT_FAT32]++; + else if (fsi->vol_type == FAT16) + statistics.mnt_cnt[SDFAT_MNT_FAT16]++; + else if (fsi->vol_type == FAT12) + statistics.mnt_cnt[SDFAT_MNT_FAT12]++; + + if (fsi->sect_per_clus_bits < SDFAT_VF_CLUS_MAX) + statistics.clus_vfat[fsi->sect_per_clus_bits]++; + else + statistics.clus_vfat[SDFAT_VF_CLUS_MAX - 1]++; +} + +void sdfat_statistics_set_mkdir(u8 flags) +{ + if (flags != 0x03) + return; + statistics.nofat_op[SDFAT_OP_MKDIR] = 1; +} + +void sdfat_statistics_set_create(u8 flags) +{ + if (flags != 0x03) + return; + statistics.nofat_op[SDFAT_OP_CREATE] = 1; +} + +/* flags : file or dir flgas, 0x03 means no fat-chain. + * clu_offset : file or dir logical cluster offset + * create : BMAP_ADD_CLUSTER or not + * + * File or dir have BMAP_ADD_CLUSTER is no fat-chain write + * when they have 0x03 flag and two or more clusters. + * And don`t have BMAP_ADD_CLUSTER is no fat-chain read + * when above same condition. + */ +void sdfat_statistics_set_rw(u8 flags, u32 clu_offset, s32 create) +{ + if ((flags == 0x03) && (clu_offset > 1)) { + if (create) + statistics.nofat_op[SDFAT_OP_WRITE] = 1; + else + statistics.nofat_op[SDFAT_OP_READ] = 1; + } +} + +/* flags : file or dir flgas, 0x03 means no fat-chain. + * clu : cluster chain + * + * Set no fat-chain trunc when file or dir have 0x03 flag + * and two or more clusters. + */ +void sdfat_statistics_set_trunc(u8 flags, CHAIN_T *clu) +{ + if ((flags == 0x03) && (clu->size > 1)) + statistics.nofat_op[SDFAT_OP_TRUNC] = 1; +} + +void sdfat_statistics_set_vol_size(struct super_block *sb) +{ + u64 vol_size; + FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); + + vol_size = (u64)fsi->num_sectors << sb->s_blocksize_bits; + + if (vol_size <= ((u64)1 << 32)) + statistics.vol_size[SDFAT_VOL_4G]++; + else if (vol_size <= ((u64)1 << 33)) + statistics.vol_size[SDFAT_VOL_8G]++; + else if (vol_size <= ((u64)1 << 34)) + statistics.vol_size[SDFAT_VOL_16G]++; + else if (vol_size <= ((u64)1 << 35)) + statistics.vol_size[SDFAT_VOL_32G]++; + else if (vol_size <= ((u64)1 << 36)) + statistics.vol_size[SDFAT_VOL_64G]++; + else if (vol_size <= ((u64)1 << 37)) + statistics.vol_size[SDFAT_VOL_128G]++; + else if (vol_size <= ((u64)1 << 38)) + statistics.vol_size[SDFAT_VOL_256G]++; + else if (vol_size <= ((u64)1 << 39)) + statistics.vol_size[SDFAT_VOL_512G]++; + else + statistics.vol_size[SDFAT_VOL_XTB]++; +} diff --git a/fs/sdfat/upcase.h b/fs/sdfat/upcase.h new file mode 100644 index 0000000000000..386772c57f8d9 --- /dev/null +++ b/fs/sdfat/upcase.h @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _UPCASE_H +#define _UPCASE_H + +/* Upcase tabel macro */ +#define SDFAT_NUM_UPCASE 2918 +#define HIGH_INDEX_BIT (8) +#define HIGH_INDEX_MASK (0xFF00) +#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) +#define UTBL_ROW_COUNT (1<> LOW_INDEX_BIT; +} +static inline u16 get_row_index(u16 i) +{ + return i & ~HIGH_INDEX_MASK; +} + + +static const u8 uni_def_upcase[SDFAT_NUM_UPCASE<<1] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, + 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, + 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, + 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, + 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, + 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, + 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, + 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, + 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, + 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, + 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, + 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, + 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, + 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, + 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, + 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, + 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, + 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, + 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, + 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, + 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, + 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, + 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, + 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, + 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, + 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, + 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, + 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, + 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, + 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, + 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, + 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, + 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, + 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, + 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, + 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, + 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, + 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, + 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, + 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, + 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, + 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, + 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, + 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, + 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, + 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, + 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, + 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, + 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, + 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, + 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, + 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, + 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, + 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, + 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, + 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, + 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, + 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, + 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, + 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, + 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, + 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, + 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, + 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, + 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, + 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, + 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, + 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, + 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, + 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, + 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, + 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, + 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, + 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, + 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, + 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, + 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, + 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, + 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, + 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, + 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, + 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, + 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, + 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, + 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, + 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, + 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, + 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, + 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, + 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, + 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, + 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, + 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, + 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, + 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, + 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, + 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, + 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, + 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, + 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, + 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, + 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, + 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, + 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, + 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, + 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, + 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, + 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, + 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, + 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, + 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, + 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, + 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, + 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, + 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, + 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, + 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, + 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, + 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, + 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, + 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, + 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, + 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, + 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, + 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, + 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, + 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, + 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, + 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, + 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, + 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, + 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, + 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, + 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, + 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, + 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, + 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, + 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, + 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, + 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, + 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, + 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, + 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, + 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, + 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, + 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, + 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, + 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, + 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, + 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, + 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, + 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, + 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, + 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, + 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, + 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, + 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, + 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, + 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, + 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, + 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, + 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, + 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, + 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, + 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, + 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, + 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, + 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, + 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, + 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, + 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, + 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, + 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, + 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, + 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, + 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, + 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, + 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, + 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, + 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, + 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, + 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, + 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, + 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, + 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, + 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, + 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, + 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, + 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, + 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, + 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, + 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, + 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, + 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, + 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, + 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, + 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, + 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, + 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, + 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, + 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, + 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, + 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, + 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, + 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, + 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, + 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, + 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, + 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, + 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, + 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, + 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, + 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, + 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, + 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, + 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, + 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, + 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, + 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, + 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, + 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, + 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, + 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, + 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, + 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, + 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, + 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, + 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, + 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, + 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, + 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, + 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, + 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, + 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, + 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, + 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, + 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, + 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, + 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, + 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, + 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, + 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, + 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, + 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, + 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, + 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, + 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, + 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, + 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, + 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, + 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, + 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, + 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, + 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, + 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, + 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, + 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, + 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, + 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, + 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, + 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, + 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, + 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, + 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, + 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, + 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, + 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, + 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, + 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, + 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, + 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, + 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, + 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, + 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, + 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, + 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, + 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, + 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, + 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, + 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, + 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, + 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, + 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, + 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF +}; + +#endif /* _UPCASE_H */ diff --git a/fs/sdfat/version.h b/fs/sdfat/version.h new file mode 100644 index 0000000000000..c49175c0228bb --- /dev/null +++ b/fs/sdfat/version.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : version.h */ +/* PURPOSE : sdFAT File Manager */ +/* */ +/************************************************************************/ +#define SDFAT_VERSION "2.0.6-lineage" diff --git a/fs/sdfat/xattr.c b/fs/sdfat/xattr.c new file mode 100644 index 0000000000000..40bb850711beb --- /dev/null +++ b/fs/sdfat/xattr.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : xattr.c */ +/* PURPOSE : sdFAT code for supporting xattr(Extended File Attributes) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/* */ +/************************************************************************/ + +#include +#include +#include +#include +#include "sdfat.h" + +#ifndef CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL +#define CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL ("undefined") +#endif + +static const char default_xattr[] = CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL; + +static int can_support(const char *name) +{ + if (!name || strcmp(name, "security.selinux")) + return -1; + return 0; +} + +ssize_t sdfat_listxattr(struct dentry *dentry, char *list, size_t size) +{ + return 0; +} + + +/************************************************************************* + * INNER FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +static int __sdfat_xattr_check_support(const char *name) +{ + if (can_support(name)) + return -EOPNOTSUPP; + + return 0; +} + +ssize_t __sdfat_getxattr(const char *name, void *value, size_t size) +{ + if (can_support(name)) + return -EOPNOTSUPP; + + if ((size > strlen(default_xattr)+1) && value) + strcpy(value, default_xattr); + + return strlen(default_xattr); +} + + +/************************************************************************* + * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) +static int sdfat_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + return __sdfat_getxattr(name, buffer, size); +} + +static int sdfat_xattr_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + return __sdfat_xattr_check_support(name); +} + +const struct xattr_handler sdfat_xattr_handler = { + .prefix = "", /* match anything */ + .get = sdfat_xattr_get, + .set = sdfat_xattr_set, +}; + +const struct xattr_handler *sdfat_xattr_handlers[] = { + &sdfat_xattr_handler, + NULL +}; + +void setup_sdfat_xattr_handler(struct super_block *sb) +{ + sb->s_xattr = sdfat_xattr_handlers; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */ +int sdfat_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) +{ + return __sdfat_xattr_check_support(name); +} + +ssize_t sdfat_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) +{ + return __sdfat_getxattr(name, value, size); +} + +int sdfat_removexattr(struct dentry *dentry, const char *name) +{ + return __sdfat_xattr_check_support(name); +} + +void setup_sdfat_xattr_handler(struct super_block *sb) +{ + /* DO NOTHING */ +} +#endif From 2849efcb4b1b1a307b3410f8ffeda8ff05106dae Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 4 Jul 2018 04:01:42 +0100 Subject: [PATCH 129/296] oneplus2: early mount vendor Change-Id: I1d35f6a4904be8d0d054c78aabc2df2193833b2a Signed-off-by: anupritaisno1 --- arch/arm/boot/dts/qcom/msm8994.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8994.dtsi b/arch/arm/boot/dts/qcom/msm8994.dtsi index f86775bb1de88..297fdc5c32b63 100644 --- a/arch/arm/boot/dts/qcom/msm8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm8994.dtsi @@ -40,6 +40,14 @@ fsmgr_flags = "wait,verify=/dev/block/platform/soc.0/f9824900.sdhci/by-name/reserve2"; status = "ok"; }; +vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/cache"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify=/dev/block/platform/soc.0/f9824900.sdhci/by-name/reserve2"; + status = "ok"; + }; }; }; }; From cbeb01aff5396fe432235c7940554c14c194c744 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 4 Jul 2018 04:03:37 +0100 Subject: [PATCH 130/296] Makefile: use strict alignment on the kernel Change-Id: I4c860297415e16fe5373dfd402f3205aa9c36039 Signed-off-by: anupritaisno1 --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7ac661f81236a..48437d4dedd73 100755 --- a/Makefile +++ b/Makefile @@ -380,7 +380,7 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ CFLAGS_MODULE = $(FLAGS_OPTIMIZE) $(GRAPHITE) AFLAGS_MODULE = $(FLAGS_OPTIMIZE) $(GRAPHITE) LDFLAGS_MODULE = --strip-debug -CFLAGS_KERNEL = $(FLAGS_OPTIMIZE) $(GRAPHITE) -mcpu=cortex-a57.cortex-a53+crypto+crc -mtune=cortex-a57.cortex-a53 -march=armv8-a+crypto+crc +CFLAGS_KERNEL = $(FLAGS_OPTIMIZE) $(GRAPHITE) -mcpu=cortex-a57.cortex-a53+crypto+crc -mtune=cortex-a57.cortex-a53 -march=armv8-a+crypto+crc -mfix-cortex-a53-843419 -mfix-cortex-a53-835769 AFLAGS_KERNEL = $(FLAGS_OPTIMIZE) $(GRAPHITE) CFLAGS_GCOV = -fprofile-arcs -ftest-coverage @@ -437,6 +437,8 @@ KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -Wno-switch-bool \ -Wno-multistatement-macros \ -Wno-bool-operation -Wno-nonnull -Wno-switch-unreachable -Wno-format-truncation -Wno-format-overflow -Wno-duplicate-decl-specifier -Wno-memset-elt-size -Wno-int-in-bool-context \ + -mstrict-align \ + -mfix-cortex-a53-843419 -mfix-cortex-a53-835769 \ $(GEN_OPT_FLAGS) \ $(GRAPHITE) \ $(FLAGS_OPTIMIZE) From 7e48715384a19f70bea62decceddf94a883b2562 Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Fri, 19 Jan 2018 13:49:21 +0530 Subject: [PATCH 131/296] msm: mdss: fix race condition between rotator api's current code does not have locking mechanism between rotator play and rotator unset, due to which race condition can occur when concurrent threads invoke rotator play and unset ioctl cmd. So use mutex lock to avoid such issues. Bug: 77527701 Change-Id: I6a7cd16ee8a8f3a4c9397e87b8c109809ec6f573 Signed-off-by: Raghavendra Ambadas Signed-off-by: Steve Pfetsch Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_mdp_rotator.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c index 1019016a58288..136375cf0cf64 100644 --- a/drivers/video/msm/mdss/mdss_mdp_rotator.c +++ b/drivers/video/msm/mdss/mdss_mdp_rotator.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 @@ -43,6 +43,7 @@ struct mdss_mdp_rot_pipe { struct mdss_mdp_rot_session_mgr { struct list_head queue; struct mutex session_lock; + struct mutex req_lock; int session_id; int session_count; @@ -77,6 +78,7 @@ int mdss_mdp_rot_mgr_init(void) mutex_init(&rot_mgr->session_lock); mutex_init(&rot_mgr->pipe_lock); + mutex_init(&rot_mgr->req_lock); INIT_LIST_HEAD(&rot_mgr->queue); rot_mgr->rot_work_queue = create_workqueue("rot_commit_workq"); if (!rot_mgr->rot_work_queue) { @@ -1017,9 +1019,11 @@ int mdss_mdp_rotator_play(struct msm_fb_data_type *mfd, u32 flgs; struct mdss_mdp_data src_buf; + mutex_lock(&rot_mgr->req_lock); rot = mdss_mdp_rot_mgr_get_session(req->id); if (!rot) { pr_err("invalid session id=%x\n", req->id); + mutex_unlock(&rot_mgr->req_lock); return -ENOENT; } @@ -1070,6 +1074,7 @@ int mdss_mdp_rotator_play(struct msm_fb_data_type *mfd, dst_buf_fail: mutex_unlock(&rot->lock); + mutex_unlock(&rot_mgr->req_lock); mdss_iommu_ctrl(0); return ret; } @@ -1079,9 +1084,11 @@ int mdss_mdp_rotator_unset(int ndx) struct mdss_mdp_rotator_session *rot; int ret = 0; + mutex_lock(&rot_mgr->req_lock); rot = mdss_mdp_rot_mgr_get_session(ndx); if (rot) ret = mdss_mdp_rotator_release(rot); + mutex_unlock(&rot_mgr->req_lock); return ret; } From b95fb4f9317f3b4b7d0dbe76fada83ad1d96811d Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Tue, 6 Mar 2018 11:04:49 -0800 Subject: [PATCH 132/296] ASoC: msm: qdsp6v2: check for buffer size before read Check for debugfs ops buf size passed before reading to eliminate the possibility of reading out of bounds. Bug: 77528653 Change-Id: I28fd60ce93256b6b0bad62b449092a891cc15463 Signed-off-by: Karthikeyan Mani Signed-off-by: anupritaisno1 --- sound/soc/msm/qdsp6v2/q6asm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 9bcf0017e954e..fa27343fd2d20 100755 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -141,6 +141,11 @@ static ssize_t audio_output_latency_dbgfs_read(struct file *file, pr_err("%s: out_buffer is null\n", __func__); return 0; } + if (count < OUT_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + OUT_BUFFER_SIZE, count); + return 0; + } snprintf(out_buffer, OUT_BUFFER_SIZE, "%ld,%ld,%ld,%ld,%ld,%ld,",\ out_cold_tv.tv_sec, out_cold_tv.tv_usec, out_warm_tv.tv_sec,\ out_warm_tv.tv_usec, out_cont_tv.tv_sec, out_cont_tv.tv_usec); @@ -194,6 +199,11 @@ static ssize_t audio_input_latency_dbgfs_read(struct file *file, pr_err("%s: in_buffer is null\n", __func__); return 0; } + if (count < IN_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + IN_BUFFER_SIZE, count); + return 0; + } snprintf(in_buffer, IN_BUFFER_SIZE, "%ld,%ld,",\ in_cont_tv.tv_sec, in_cont_tv.tv_usec); return simple_read_from_buffer(buf, IN_BUFFER_SIZE, ppos, From f48160cfe6409cf35086763a582d605a770bc91c Mon Sep 17 00:00:00 2001 From: Mahesh Sivasubramanian Date: Tue, 28 Nov 2017 10:06:17 -0700 Subject: [PATCH 133/296] 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. Bug: 72956998 Change-Id: I0e0fd5dd9f251b5093d6e9d6335387512ec59249 Signed-off-by: Mahesh Sivasubramanian Signed-off-by: anupritaisno1 --- 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 5926916fce5fe..b6979cd744cf2 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -574,14 +574,12 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) 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) { From 6764ba9da799487a35e484902f183abe7c6ae5cd Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 14 Feb 2018 15:45:07 -0800 Subject: [PATCH 134/296] NFC: llcp: Limit size of SDP URI [ Upstream commit fe9c842695e26d8116b61b80bfb905356f07834b ] The tlv_len is u8, so we need to limit the size of the SDP URI. Enforce this both in the NLA policy and in the code that performs the allocation and copy, to avoid writing past the end of the allocated buffer. Fixes: d9b8d8e19b073 ("NFC: llcp: Service Name Lookup netlink interface") Signed-off-by: Kees Cook Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman Signed-off-by: anupritaisno1 --- net/nfc/llcp_commands.c | 4 ++++ net/nfc/netlink.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index c1b23eef83ca9..ac7cf1faed18c 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -151,6 +151,10 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri, pr_debug("uri: %s, len: %zu\n", uri, uri_len); + /* sdreq->tlv_len is u8, takes uri_len, + 3 for header, + 1 for NULL */ + if (WARN_ON_ONCE(uri_len > U8_MAX - 4)) + return NULL; + sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); if (sdreq == NULL) return NULL; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index f0c4d61f37c0f..532869d8bc359 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -59,7 +59,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { }; static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { - [NFC_SDP_ATTR_URI] = { .type = NLA_STRING }, + [NFC_SDP_ATTR_URI] = { .type = NLA_STRING, + .len = U8_MAX - 4 }, [NFC_SDP_ATTR_SAP] = { .type = NLA_U8 }, }; From 5cca30d35db0bf457c1d5205649ea59d93493d51 Mon Sep 17 00:00:00 2001 From: Dennis Cagle Date: Fri, 13 Apr 2018 11:49:03 -0700 Subject: [PATCH 135/296] 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 Bug: 63165135 CRs-Fixed: 2139538 Signed-off-by: Dennis Cagle (cherry picked from commit c29e11c774b3c59660c1c599b73b7fabf1492d43) Signed-off-by: David Lin Signed-off-by: anupritaisno1 --- drivers/char/adsprpc.c | 92 +++++++++++++++++------------------ drivers/char/adsprpc_compat.c | 14 +++--- drivers/char/adsprpc_shared.h | 25 +++++----- 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 258c3c9782675..e08f4c6d65f56 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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 @@ -80,7 +80,7 @@ static inline uintptr_t buf_page_offset(void *buf) return offset; } -static inline int buf_num_pages(void *buf, ssize_t len) +static inline int buf_num_pages(void *buf, size_t len) { uintptr_t start = buf_page_start(buf) >> PAGE_SHIFT; uintptr_t end = (((uintptr_t) buf + len - 1) & PAGE_MASK) >> PAGE_SHIFT; @@ -152,7 +152,7 @@ struct fastrpc_buf { struct ion_handle *handle; void *virt; ion_phys_addr_t phys; - ssize_t size; + size_t size; int used; }; @@ -239,7 +239,7 @@ struct fastrpc_mmap { ion_phys_addr_t phys; uintptr_t *vaddrin; uintptr_t vaddrout; - ssize_t size; + size_t size; int refs; }; @@ -287,7 +287,7 @@ static int map_iommu_mem(struct ion_handle *handle, struct file_data *fdata, ion_phys_addr_t *iova, unsigned long size) { struct fastrpc_apps *me = &gfa; - struct fastrpc_mmap *map = 0, *mapmatch = 0; + struct fastrpc_mmap *map = NULL, *mapmatch = NULL; struct hlist_node *n; unsigned long len = size; int cid = fdata->cid; @@ -321,7 +321,7 @@ static void unmap_iommu_mem(struct ion_handle *handle, struct file_data *fdata, int cached) { struct fastrpc_apps *me = &gfa; - struct fastrpc_mmap *map = 0, *mapmatch = 0; + struct fastrpc_mmap *map = NULL, *mapmatch = NULL; struct hlist_node *n; int cid = fdata->cid; @@ -356,10 +356,10 @@ static void free_mem(struct fastrpc_buf *buf, struct file_data *fd) } if (!IS_ERR_OR_NULL(buf->virt)) { ion_unmap_kernel(me->iclient, buf->handle); - buf->virt = 0; + buf->virt = NULL; } ion_free(me->iclient, buf->handle); - buf->handle = 0; + buf->handle = NULL; } } @@ -375,11 +375,11 @@ static void free_map(struct fastrpc_mmap *map, struct file_data *fdata) } if (!IS_ERR_OR_NULL(map->virt)) { ion_unmap_kernel(me->iclient, map->handle); - map->virt = 0; + map->virt = NULL; } ion_free(me->iclient, map->handle); } - map->handle = 0; + map->handle = NULL; } static int alloc_mem(struct fastrpc_buf *buf, struct file_data *fdata) @@ -390,8 +390,8 @@ static int alloc_mem(struct fastrpc_buf *buf, struct file_data *fdata) int err = 0; int cid = fdata->cid; unsigned int heap; - buf->handle = 0; - buf->virt = 0; + buf->handle = NULL; + buf->virt = NULL; buf->phys = 0; heap = me->channel[cid].smmu.enabled ? ION_HEAP(ION_IOMMU_HEAP_ID) : ION_HEAP(ION_ADSP_HEAP_ID); @@ -426,7 +426,7 @@ static int context_restore_interrupted(struct fastrpc_apps *me, 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(&me->clst.hlock); @@ -522,7 +522,7 @@ static int context_alloc(struct fastrpc_apps *me, uint32_t kernel, struct smq_invoke_ctx **po) { int err = 0, bufs, ii, size = 0; - struct smq_invoke_ctx *ctx = 0; + struct smq_invoke_ctx *ctx = NULL; struct smq_context_list *clst = &me->clst; struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; @@ -536,7 +536,7 @@ static int context_alloc(struct fastrpc_apps *me, uint32_t kernel, bufs * sizeof(*ctx->handles); } - VERIFY(err, 0 != (ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL))); + VERIFY(err, NULL != (ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL))); if (err) goto bail; @@ -545,8 +545,8 @@ static int context_alloc(struct fastrpc_apps *me, uint32_t kernel, ctx->apps = me; ctx->fdata = fdata; ctx->pra = (remote_arg_t *)(&ctx[1]); - ctx->fds = invokefd->fds == 0 ? 0 : (int *)(&ctx->pra[bufs]); - ctx->handles = invokefd->fds == 0 ? 0 : + ctx->fds = invokefd->fds == NULL ? NULL : (int *)(&ctx->pra[bufs]); + ctx->handles = invokefd->fds == NULL ? NULL : (struct ion_handle **)(&ctx->fds[bufs]); if (!kernel) { VERIFY(err, 0 == copy_from_user(ctx->pra, invoke->pra, @@ -681,7 +681,7 @@ static void context_notify_user(struct smq_invoke_ctx *ctx, int retval) static void context_notify_all_users(struct smq_context_list *me, int cid) { - struct smq_invoke_ctx *ictx = 0; + struct smq_invoke_ctx *ictx = NULL; struct hlist_node *n; spin_lock(&me->hlock); hlist_for_each_entry_safe(ictx, n, &me->pending, hn) { @@ -706,10 +706,10 @@ static void context_list_ctor(struct smq_context_list *me) static void context_list_dtor(struct fastrpc_apps *me, struct smq_context_list *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(&clst->hlock); hlist_for_each_entry_safe(ictx, n, &clst->interrupted, hn) { hlist_del(&ictx->hn); @@ -742,7 +742,7 @@ static int get_page_list(uint32_t kernel, struct smq_invoke_ctx *ctx) struct fastrpc_buf *ibuf = &ctx->dev->buf; struct fastrpc_buf *obuf = &ctx->obuf; remote_arg_t *pra = ctx->pra; - ssize_t rlen; + size_t rlen; uint32_t sc = ctx->sc; int cid = ctx->fdata->cid; int i, err = 0; @@ -769,7 +769,7 @@ static int get_page_list(uint32_t kernel, struct smq_invoke_ctx *ctx) for (i = 0; i < inbufs + outbufs; ++i) { void *buf; int num; - ssize_t len; + size_t len; list[i].num = 0; list[i].pgidx = 0; @@ -876,14 +876,14 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx, { struct fastrpc_apps *me = &gfa; struct smq_invoke_buf *list; - struct fastrpc_buf *pbuf = &ctx->obuf, *obufs = 0; + struct fastrpc_buf *pbuf = &ctx->obuf, *obufs = NULL; struct smq_phy_page *pages; struct vm_area_struct *vma; struct ion_handle **handles = ctx->handles; void *args; remote_arg_t *pra = ctx->pra; remote_arg_t *rpra = ctx->rpra; - ssize_t rlen, used, size, copylen = 0; + size_t rlen, used, size, copylen = 0; uint32_t sc = ctx->sc, start; int i, inh, bufs = 0, err = 0, oix; int inbufs = REMOTE_SCALARS_INBUFS(sc); @@ -976,7 +976,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx, /* copy non ion buffers */ for (oix = 0; oix < inbufs + outbufs; ++oix) { int i = ctx->overps[oix]->raix; - ssize_t mlen = ctx->overps[oix]->mend - ctx->overps[oix]->mstart; + size_t mlen = ctx->overps[oix]->mend - ctx->overps[oix]->mstart; if (!pra[i].buf.len) continue; if (list[i].num) @@ -1271,7 +1271,7 @@ static void free_dev(struct fastrpc_device *dev, struct file_data *fdata) static int alloc_dev(struct fastrpc_device **dev, struct file_data *fdata) { int err = 0; - struct fastrpc_device *fd = 0; + struct fastrpc_device *fd = NULL; VERIFY(err, 0 != try_module_get(THIS_MODULE)); if (err) @@ -1299,7 +1299,7 @@ static int get_dev(struct fastrpc_apps *me, struct file_data *fdata, struct fastrpc_device **rdev) { struct hlist_head *head; - struct fastrpc_device *dev = 0, *devfree = 0; + struct fastrpc_device *dev = NULL, *devfree = NULL; struct hlist_node *n; uint32_t h = hash_32(current->tgid, RPC_HASH_BITS); int err = 0; @@ -1345,7 +1345,7 @@ static int fastrpc_internal_invoke(struct fastrpc_apps *me, uint32_t mode, struct fastrpc_ioctl_invoke_fd *invokefd, struct file_data *fdata) { - struct smq_invoke_ctx *ctx = 0; + struct smq_invoke_ctx *ctx = NULL; struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; int cid = fdata->cid; int interrupted = 0; @@ -1434,8 +1434,8 @@ static int fastrpc_init_process(struct file_data *fdata, { int err = 0; struct fastrpc_ioctl_invoke_fd ioctl; - struct smq_phy_page *pages = 0; - struct fastrpc_mmap *map = 0; + struct smq_phy_page *pages = NULL; + struct fastrpc_mmap *map = NULL; int npages = 0; struct fastrpc_apps *me = &gfa; if (init->flags == FASTRPC_INIT_ATTACH) { @@ -1590,7 +1590,7 @@ static int fastrpc_munmap_on_dsp(struct fastrpc_apps *me, struct { int pid; uintptr_t vaddrout; - ssize_t size; + size_t size; } inargs; inargs.pid = current->tgid; @@ -1644,9 +1644,9 @@ static int map_buffer(struct fastrpc_apps *me, struct file_data *fdata, struct smq_phy_page **ppages, int *pnpages) { struct ion_client *clnt = gfa.iclient; - struct ion_handle *handle = 0; - struct fastrpc_mmap *map = 0, *mapmatch = 0; - struct smq_phy_page *pages = 0; + struct ion_handle *handle = NULL; + struct fastrpc_mmap *map = NULL, *mapmatch = NULL; + struct smq_phy_page *pages = NULL; struct hlist_node *n; uintptr_t vaddrout = 0; int num; @@ -1723,8 +1723,8 @@ static int fastrpc_internal_mmap(struct fastrpc_apps *me, struct fastrpc_ioctl_mmap *mmap) { - struct fastrpc_mmap *map = 0; - struct smq_phy_page *pages = 0; + struct fastrpc_mmap *map = NULL; + struct smq_phy_page *pages = NULL; int num = 0; int err = 0; VERIFY(err, 0 == map_buffer(me, fdata, mmap->fd, (char *)mmap->vaddrin, @@ -1754,7 +1754,7 @@ static void cleanup_current_dev(struct file_data *fdata) struct fastrpc_device *dev, *devfree; rnext: - devfree = dev = 0; + devfree = dev = NULL; spin_lock(&me->hlock); head = &me->htbl[h]; hlist_for_each_entry_safe(dev, n, head, hn) { @@ -1792,9 +1792,9 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) struct file_data *fdata = (struct file_data *)file->private_data; struct fastrpc_apps *me = &gfa; struct smq_context_list *clst = &me->clst; - struct smq_invoke_ctx *ictx = 0, *ctxfree; + struct smq_invoke_ctx *ictx = NULL, *ctxfree; struct hlist_node *n; - struct fastrpc_mmap *map = 0; + struct fastrpc_mmap *map = NULL; int cid = MINOR(inode->i_rdev); if (!fdata) @@ -1802,7 +1802,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) (void)fastrpc_release_current_dsp_process(fdata); do { - ctxfree = 0; + ctxfree = NULL; spin_lock(&clst->hlock); hlist_for_each_entry_safe(ictx, n, &clst->interrupted, hn) { if ((ictx->tgid == current->tgid) && @@ -1840,7 +1840,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) mutex_lock(&me->smd_mutex); 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)) { VERIFY(err, 0 == smd_named_open_on_edge( FASTRPC_SMD_GUID, gcinfo[cid].channel, @@ -1860,9 +1860,9 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) } mutex_unlock(&me->smd_mutex); - filp->private_data = 0; + filp->private_data = NULL; if (0 != try_module_get(THIS_MODULE)) { - struct file_data *fdata = 0; + struct file_data *fdata = NULL; /* This call will cause a dev to be created * which will addref this module */ @@ -1892,7 +1892,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) completion_bail: smd_close(me->channel[cid].chan); - me->channel[cid].chan = 0; + me->channel[cid].chan = NULL; smd_bail: mutex_unlock(&me->smd_mutex); return err; @@ -1914,7 +1914,7 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num, switch (ioctl_num) { case FASTRPC_IOCTL_INVOKE_FD: case FASTRPC_IOCTL_INVOKE: - invokefd.fds = 0; + invokefd.fds = NULL; size = (ioctl_num == FASTRPC_IOCTL_INVOKE) ? sizeof(invokefd.inv) : sizeof(invokefd); VERIFY(err, 0 == copy_from_user(&invokefd, param, size)); @@ -1992,7 +1992,7 @@ static int fastrpc_restart_notifier_cb(struct notifier_block *nb, ctx->ssrcount++; if (ctx->chan) { smd_close(ctx->chan); - ctx->chan = 0; + ctx->chan = NULL; pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name, MAJOR(me->dev_no), cid); } diff --git a/drivers/char/adsprpc_compat.c b/drivers/char/adsprpc_compat.c index ee324dcf91f0e..160ad1851c9a8 100644 --- a/drivers/char/adsprpc_compat.c +++ b/drivers/char/adsprpc_compat.c @@ -1,5 +1,5 @@ /* - * 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 @@ -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 c8e31369cef16..b8e5f23a56c04 100644 --- a/drivers/char/adsprpc_shared.h +++ b/drivers/char/adsprpc_shared.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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 @@ -93,7 +93,7 @@ do {\ struct remote_buf { void *pv; /* buffer pointer */ - ssize_t len; /* length of buffer */ + size_t len; /* length of buffer */ }; union remote_arg { @@ -114,25 +114,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 */ }; @@ -144,7 +144,7 @@ struct smq_null_invoke { struct smq_phy_page { unsigned long addr; /* physical address */ - ssize_t size; /* size of contiguous region */ + size_t size; /* size of contiguous region */ }; struct smq_invoke_buf { @@ -171,14 +171,15 @@ struct smq_invoke_rsp { static inline struct smq_invoke_buf *smq_invoke_buf_start(remote_arg_t *pra, uint32_t sc) { - int len = REMOTE_SCALARS_LENGTH(sc); + unsigned int 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]); } From 0d24559b9917347d42f4e2a901700fdec1d9a38d Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Mon, 10 Apr 2017 19:56:25 -0700 Subject: [PATCH 136/296] soc: q6dspv2: apr: fix client registration refcount Audio Packet Router (APR) is used by multiple audio services to communicate between APSS and ADSP. These audio services registers for service level APR communication (port 0xFFFFFFFF), or for session level APR communication (using port 0x101 etc.). The services might choose to call apr_register for any port at random. The expectation is that the refcounting for the number of ports registered with APR for any specific service, is handled irrespective of the order in which registrations are done. The current logic fails to handle the refcounting when apr_register is called for 0xFFFFFFFF before other session based ports. Fix this correctly using the service count (svc_cnt) variable in apr_svc. CRs-fixed: 2022490 Bug: 34088848 Change-Id: I2fcd1269facf24d509db0d90314e0d2545a2ad67 Signed-off-by: Banajit Goswami Signed-off-by: anupritaisno1 # Conflicts: # drivers/soc/qcom/qdsp6v2/apr.c --- drivers/soc/qcom/qdsp6v2/apr.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c index cb4cda4f7f313..7c945dff8daf0 100644 --- a/drivers/soc/qcom/qdsp6v2/apr.c +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2015, 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-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 @@ -404,19 +404,19 @@ struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, mutex_unlock(&svc->m_lock); return NULL; } - if (!svc->port_cnt && !svc->svc_cnt) + if (!svc->svc_cnt) clnt->svc_cnt++; svc->port_cnt++; svc->port_fn[temp_port] = svc_fn; svc->port_priv[temp_port] = priv; + svc->svc_cnt++; } else { if (!svc->fn) { - if (!svc->port_cnt && !svc->svc_cnt) + if (!svc->svc_cnt) clnt->svc_cnt++; svc->fn = svc_fn; - if (svc->port_cnt) - svc->svc_cnt++; svc->priv = priv; + svc->svc_cnt++; } } @@ -622,24 +622,17 @@ int apr_deregister(void *handle) client_id = svc->client_id; clnt = &client[dest_id][client_id]; - if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (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) { + svc->svc_cnt--; + if (!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 %pK\n", __func__, svc); } } - if (!svc->port_cnt && !svc->svc_cnt) { + if (!svc->svc_cnt) { svc->priv = NULL; svc->id = 0; svc->fn = NULL; From 20063c8d2d06e86c862e45ea915aa28c9c42357e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 26 Mar 2018 15:39:07 -1000 Subject: [PATCH 137/296] UPSTREAM: perf/hwbp: Simplify the perf-hwbp code, fix documentation Annoyingly, modify_user_hw_breakpoint() unnecessarily complicates the modification of a breakpoint - simplify it and remove the pointless local variables. Also update the stale Docbook while at it. Signed-off-by: Linus Torvalds Acked-by: Thomas Gleixner Cc: Cc: Alexander Shishkin Cc: Andy Lutomirski Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Vince Weaver Signed-off-by: Ingo Molnar Bug: 110918800 Change-Id: Ifbfc57ae4a1aa708d673aff91206a278201b130d (cherry picked from commit f67b15037a7a50c57f72e69a6d59941ad90a0f0f) Signed-off-by: Steve Muckle Signed-off-by: anupritaisno1 --- kernel/events/hw_breakpoint.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 20185ea64aa6f..390846433239a 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -445,16 +445,9 @@ EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); * modify_user_hw_breakpoint - modify a user-space hardware breakpoint * @bp: the breakpoint structure to modify * @attr: new breakpoint attributes - * @triggered: callback to trigger when we hit the breakpoint - * @tsk: pointer to 'task_struct' of the process to which the address belongs */ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) { - u64 old_addr = bp->attr.bp_addr; - u64 old_len = bp->attr.bp_len; - int old_type = bp->attr.bp_type; - int err = 0; - /* * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it * will not be possible to raise IPIs that invoke __perf_event_disable. @@ -469,27 +462,18 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att bp->attr.bp_addr = attr->bp_addr; bp->attr.bp_type = attr->bp_type; bp->attr.bp_len = attr->bp_len; + bp->attr.disabled = 1; - if (attr->disabled) - goto end; - - err = validate_hw_breakpoint(bp); - if (!err) - perf_event_enable(bp); + if (!attr->disabled) { + int err = validate_hw_breakpoint(bp); - if (err) { - bp->attr.bp_addr = old_addr; - bp->attr.bp_type = old_type; - bp->attr.bp_len = old_len; - if (!bp->attr.disabled) - perf_event_enable(bp); + if (err) + return err; - return err; + perf_event_enable(bp); + bp->attr.disabled = 0; } -end: - bp->attr.disabled = attr->disabled; - return 0; } EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); From 7d18576a119596ca6b802bdf66a38641445bde8b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Jul 2018 12:27:06 +0200 Subject: [PATCH 138/296] UPSTREAM: tcp: avoid collapses in tcp_prune_queue() if possible [ Upstream commit f4a3313d8e2ca9fd8d8f45e40a2903ba782607e7 ] Right after a TCP flow is created, receiving tiny out of order packets allways hit the condition : if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) tcp_clamp_window(sk); tcp_clamp_window() increases sk_rcvbuf to match sk_rmem_alloc (guarded by tcp_rmem[2]) Calling tcp_collapse_ofo_queue() in this case is not useful, and offers a O(N^2) surface attack to malicious peers. Better not attempt anything before full queue capacity is reached, forcing attacker to spend lots of resource and allow us to more easily detect the abuse. Change-Id: Ib4fabbd6f22b51fd6eea66a0f3b210543d3ebe01 Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Yuchung Cheng Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman Signed-off-by: Chenbo Feng Signed-off-by: anupritaisno1 --- net/ipv4/tcp_input.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 3a4b3b8810d92..3d3ba6dacb479 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4676,6 +4676,9 @@ static int tcp_prune_queue(struct sock *sk) else if (sk_under_memory_pressure(sk)) tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss); + if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) + return 0; + tcp_collapse_ofo_queue(sk); if (!skb_queue_empty(&sk->sk_receive_queue)) tcp_collapse(sk, &sk->sk_receive_queue, From 3d3f47a37011dbfacbc7c3275a6fbeac2499a1e2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Jul 2018 12:27:07 +0200 Subject: [PATCH 139/296] UPSTREAM: tcp: detect malicious patterns in tcp_collapse_ofo_queue() [ Upstream commit 3d4bf93ac12003f9b8e1e2de37fe27983deebdcf ] In case an attacker feeds tiny packets completely out of order, tcp_collapse_ofo_queue() might scan the whole rb-tree, performing expensive copies, but not changing socket memory usage at all. 1) Do not attempt to collapse tiny skbs. 2) Add logic to exit early when too many tiny skbs are detected. We prefer not doing aggressive collapsing (which copies packets) for pathological flows, and revert to tcp_prune_ofo_queue() which will be less expensive. In the future, we might add the possibility of terminating flows that are proven to be malicious. Change-Id: I635a058ea387b224d1d0ac7653cc4dfc0aadab3a Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman Signed-off-by: Chenbo Feng Signed-off-by: anupritaisno1 --- net/ipv4/tcp_input.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 3d3ba6dacb479..5f7d9a8944da3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4590,6 +4590,7 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, static void tcp_collapse_ofo_queue(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); + u32 range_truesize, sum_tiny = 0; struct sk_buff *skb = skb_peek(&tp->out_of_order_queue); struct sk_buff *head; u32 start, end; @@ -4599,6 +4600,7 @@ static void tcp_collapse_ofo_queue(struct sock *sk) start = TCP_SKB_CB(skb)->seq; end = TCP_SKB_CB(skb)->end_seq; + range_truesize = skb->truesize; head = skb; for (;;) { @@ -4613,14 +4615,24 @@ static void tcp_collapse_ofo_queue(struct sock *sk) if (!skb || after(TCP_SKB_CB(skb)->seq, end) || before(TCP_SKB_CB(skb)->end_seq, start)) { - tcp_collapse(sk, &tp->out_of_order_queue, - head, skb, start, end); + /* Do not attempt collapsing tiny skbs */ + if (range_truesize != head->truesize || + end - start >= SKB_WITH_OVERHEAD(SK_MEM_QUANTUM)) { + tcp_collapse(sk, &tp->out_of_order_queue, + head, skb, start, end); + } else { + sum_tiny += range_truesize; + if (sum_tiny > sk->sk_rcvbuf >> 3) + return; + } + head = skb; if (!skb) break; /* Start new segment */ start = TCP_SKB_CB(skb)->seq; end = TCP_SKB_CB(skb)->end_seq; + range_truesize = skb->truesize; } else { if (before(TCP_SKB_CB(skb)->seq, start)) start = TCP_SKB_CB(skb)->seq; From 4da60ccd5b62fb7de096e86b1f103a3f44efb3c9 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 10 Jan 2017 16:11:07 -0800 Subject: [PATCH 140/296] ANDROID: uid_sys_stats: rename uid_cputime.c to uid_sys_stats.c This module tracks cputime and io stats. Signed-off-by: Jin Qian Bug: 34198239 Change-Id: I9ee7d9e915431e0bb714b36b5a2282e1fdcc7342 Signed-off-by: anupritaisno1 --- drivers/misc/Kconfig | 5 +++-- drivers/misc/Makefile | 3 +-- drivers/misc/{uid_cputime.c => uid_sys_stats.c} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename drivers/misc/{uid_cputime.c => uid_sys_stats.c} (100%) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ff1f282a734e4..76ef801ea5e00 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -541,11 +541,12 @@ config SRAM the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. -config UID_CPUTIME - tristate "Per-UID cpu time statistics" +config UID_SYS_STATS + tristate "Per-UID statistics" depends on PROFILING help Per UID based cpu time statistics exported to /proc/uid_cputime + Per UID based io statistics exported to /proc/uid_io config TSIF depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 94e0fe34ae72f..e873f9bae1953 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -56,7 +56,6 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o -obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o obj-$(CONFIG_TSIF) += msm_tsif.o obj-$(CONFIG_HAPTIC_ISA1200) += isa1200.o obj-$(CONFIG_QSEECOM) += qseecom.o @@ -72,4 +71,4 @@ obj-y += qcom/ #shankai@bsp, 2015/2/2, Add for :drv2605 kernel support obj-$(CONFIG_TI_DRV2605) += ti_drv2605.o #endif VENDOR_EDIT - +obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_sys_stats.c similarity index 100% rename from drivers/misc/uid_cputime.c rename to drivers/misc/uid_sys_stats.c From 759a6d923dddfbbe6c9a8440911121cf64385536 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 10 Jan 2017 16:10:35 -0800 Subject: [PATCH 141/296] ANDROID: uid_cputime: add per-uid IO usage accounting IO usages are accounted in foreground and background buckets. For each uid, io usage is calculated in two steps. delta = current total of all uid tasks - previus total current bucket += delta Bucket is determined by current uid stat. Userspace writes to /proc/uid_procstat/set when uid stat is updated. /proc/uid_io/stats shows IO usage in this format. Signed-off-by: Jin Qian Bug: 34198239 Change-Id: I3369e59e063b1e5ee0dfe3804c711d93cb937c0c Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 249 ++++++++++++++++++++++++++++++++--- 1 file changed, 233 insertions(+), 16 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index c751188ce2aba..09cfa89c4a523 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -30,7 +30,24 @@ DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); static DEFINE_MUTEX(uid_lock); -static struct proc_dir_entry *parent; +static struct proc_dir_entry *cpu_parent; +static struct proc_dir_entry *io_parent; +static struct proc_dir_entry *proc_parent; + +struct io_stats { + u64 read_bytes; + u64 write_bytes; + u64 rchar; + u64 wchar; +}; + +#define UID_STATE_FOREGROUND 0 +#define UID_STATE_BACKGROUND 1 +#define UID_STATE_BUCKET_SIZE 2 + +#define UID_STATE_TOTAL_CURR 2 +#define UID_STATE_TOTAL_LAST 3 +#define UID_STATE_SIZE 4 struct uid_entry { uid_t uid; @@ -40,6 +57,8 @@ struct uid_entry { cputime_t active_stime; unsigned long long active_power; unsigned long long power; + int state; + struct io_stats io[UID_STATE_SIZE]; struct hlist_node hash; }; @@ -72,7 +91,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) return uid_entry; } -static int uid_stat_show(struct seq_file *m, void *v) +static int uid_cputime_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; struct task_struct *task, *temp; @@ -131,13 +150,13 @@ static int uid_stat_show(struct seq_file *m, void *v) return 0; } -static int uid_stat_open(struct inode *inode, struct file *file) +static int uid_cputime_open(struct inode *inode, struct file *file) { - return single_open(file, uid_stat_show, PDE_DATA(inode)); + return single_open(file, uid_cputime_show, PDE_DATA(inode)); } -static const struct file_operations uid_stat_fops = { - .open = uid_stat_open, +static const struct file_operations uid_cputime_fops = { + .open = uid_cputime_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -196,6 +215,175 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; +static void add_uid_io_curr_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + + io_curr->read_bytes += task->ioac.read_bytes; + io_curr->write_bytes += + task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_curr->rchar += task->ioac.rchar; + io_curr->wchar += task->ioac.wchar; +} + +static void clean_uid_io_last_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_last->read_bytes -= task->ioac.read_bytes; + io_last->write_bytes -= + task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_last->rchar -= task->ioac.rchar; + io_last->wchar -= task->ioac.wchar; +} + +static void update_io_stats_locked(void) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + unsigned long bkt; + + BUG_ON(!mutex_is_locked(&uid_lock)); + + hash_for_each(hash_table, bkt, uid_entry, hash) + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + read_lock(&tasklist_lock); + do_each_thread(temp, task) { + uid_entry = find_or_register_uid(from_kuid_munged( + current_user_ns(), task_uid(task))); + if (!uid_entry) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + } +} + +static int uid_io_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + unsigned long bkt; + + mutex_lock(&uid_lock); + + update_io_stats_locked(); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu\n", + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes); + } + + mutex_unlock(&uid_lock); + + return 0; +} + +static int uid_io_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_io_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_io_fops = { + .open = uid_io_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_procstat_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_procstat_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct task_struct *task, *temp; + struct uid_entry *uid_entry; + uid_t uid, task_uid; + int argc, state; + char input[128]; + + if (count >= sizeof(input)) + return -EINVAL; + + if (copy_from_user(input, buffer, count)) + return -EFAULT; + + input[count] = '\0'; + + argc = sscanf(input, "%u %d", &uid, &state); + if (argc != 2) + return -EINVAL; + + if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) + return -EINVAL; + + mutex_lock(&uid_lock); + + uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->state == state) { + mutex_unlock(&uid_lock); + return -EINVAL; + } + + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + read_lock(&tasklist_lock); + do_each_thread(temp, task) { + task_uid = from_kuid_munged(current_user_ns(), task_uid(task)); + if (uid != task_uid) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + read_unlock(&tasklist_lock); + + update_io_stats_locked(); + + uid_entry->state = state; + + mutex_unlock(&uid_lock); + + return count; +} + +static const struct file_operations uid_procstat_fops = { + .open = uid_procstat_open, + .release = single_release, + .write = uid_procstat_write, +}; + static int process_notifier(struct notifier_block *self, unsigned long cmd, void *v) { @@ -221,6 +409,9 @@ static int process_notifier(struct notifier_block *self, uid_entry->power += task->cpu_power; task->cpu_power = ULLONG_MAX; + update_io_stats_locked(); + clean_uid_io_last_stats(uid_entry, task); + exit: mutex_unlock(&uid_lock); return NOTIFY_OK; @@ -230,25 +421,51 @@ static struct notifier_block process_notifier_block = { .notifier_call = process_notifier, }; -static int __init proc_uid_cputime_init(void) +static int __init proc_uid_sys_stats_init(void) { hash_init(hash_table); - parent = proc_mkdir("uid_cputime", NULL); - if (!parent) { - pr_err("%s: failed to create proc entry\n", __func__); - return -ENOMEM; + cpu_parent = proc_mkdir("uid_cputime", NULL); + if (!cpu_parent) { + pr_err("%s: failed to create uid_cputime proc entry\n", + __func__); + goto err; } - proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, - NULL); + proc_create_data("remove_uid_range", 0222, cpu_parent, + &uid_remove_fops, NULL); + proc_create_data("show_uid_stat", 0444, cpu_parent, + &uid_cputime_fops, NULL); - proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, - NULL); + io_parent = proc_mkdir("uid_io", NULL); + if (!io_parent) { + pr_err("%s: failed to create uid_io proc entry\n", + __func__); + goto err; + } + + proc_create_data("stats", 0444, io_parent, + &uid_io_fops, NULL); + + proc_parent = proc_mkdir("uid_procstat", NULL); + if (!io_parent) { + pr_err("%s: failed to create uid_procstat proc entry\n", + __func__); + goto err; + } + + proc_create_data("set", 0222, proc_parent, + &uid_procstat_fops, NULL); profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); return 0; + +err: + remove_proc_subtree("uid_cputime", NULL); + remove_proc_subtree("uid_io", NULL); + remove_proc_subtree("uid_procstat", NULL); + return -ENOMEM; } -early_initcall(proc_uid_cputime_init); +early_initcall(proc_uid_sys_stats_init); From 1714fc463456524b0c14f29918a7e59c77159c67 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 17 Jan 2017 17:26:07 -0800 Subject: [PATCH 142/296] ANDROID: uid_sys_stats: allow writing same state Signed-off-by: Jin Qian Bug: 34360629 Change-Id: Ia748351e07910b1febe54f0484ca1be58c4eb9c7 Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 09cfa89c4a523..1491456b23ccc 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -352,11 +352,16 @@ static ssize_t uid_procstat_write(struct file *file, mutex_lock(&uid_lock); uid_entry = find_or_register_uid(uid); - if (!uid_entry || uid_entry->state == state) { + if (!uid_entry) { mutex_unlock(&uid_lock); return -EINVAL; } + if (uid_entry->state == state) { + mutex_unlock(&uid_lock); + return 0; + } + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); From 9e742d6f1f4d4489b8a6c68e9cd56031cbe4901f Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 19 Jan 2017 16:34:34 -0800 Subject: [PATCH 143/296] ANDROID: uid_sys_stats: return full size when state is not changed. Userspace keeps retrying when it sees nothing is written. Bug: 34364961 Change-Id: Ie288c90c6a206fb863dcad010094fcd1373767aa Signed-off-by: anupritaisno1 --- 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 1491456b23ccc..61422de6f7bf4 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -359,7 +359,7 @@ static ssize_t uid_procstat_write(struct file *file, if (uid_entry->state == state) { mutex_unlock(&uid_lock); - return 0; + return count; } memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, From 12c69946624ff7bbabf275627b04ff0a567f0526 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 16 Feb 2017 18:07:05 -0800 Subject: [PATCH 144/296] ANDROID: uid_sys_stats: remove unnecessary code in procstat switch No need to aggregate the switched uid separately since update_io_stats_locked covers all uids. Bug: 34198239 Change-Id: Ifed347264b910de02e3f3c8dec95d1a2dbde58c0 Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 61422de6f7bf4..2652bb6d2e588 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -328,9 +328,8 @@ static int uid_procstat_open(struct inode *inode, struct file *file) static ssize_t uid_procstat_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - struct task_struct *task, *temp; struct uid_entry *uid_entry; - uid_t uid, task_uid; + uid_t uid; int argc, state; char input[128]; @@ -362,18 +361,6 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, - sizeof(struct io_stats)); - - read_lock(&tasklist_lock); - do_each_thread(temp, task) { - task_uid = from_kuid_munged(current_user_ns(), task_uid(task)); - if (uid != task_uid) - continue; - add_uid_io_curr_stats(uid_entry, task); - } while_each_thread(temp, task); - read_unlock(&tasklist_lock); - update_io_stats_locked(); uid_entry->state = state; From f12bb77ce17cac73e8ff2047497b424d46284234 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 28 Feb 2017 15:09:42 -0800 Subject: [PATCH 145/296] ANDROID: uid_sys_stats: fix negative write bytes. A task can cancel writes made by other tasks. In rare cases, cancelled_write_bytes is larger than write_bytes if the task itself didn't make any write. This doesn't affect total size but may cause confusion when looking at IO usage on individual tasks. Bug: 35851986 Change-Id: If6cb549aeef9e248e18d804293401bb2b91918ca Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 2652bb6d2e588..bdb440e2fb059 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -215,14 +215,21 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} + static void add_uid_io_curr_stats(struct uid_entry *uid_entry, struct task_struct *task) { struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += - task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_curr->write_bytes += compute_write_bytes(task); io_curr->rchar += task->ioac.rchar; io_curr->wchar += task->ioac.wchar; } @@ -233,8 +240,7 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= - task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_last->write_bytes -= compute_write_bytes(task); io_last->rchar -= task->ioac.rchar; io_last->wchar -= task->ioac.wchar; } From 261ab7dac9f1cc01b76605c8f524768e0d6b5aec Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 2 Mar 2017 13:32:59 -0800 Subject: [PATCH 146/296] ANDROID: sched: add a counter to track fsync Change-Id: I6c138de5b2332eea70f57e098134d1d141247b3f Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- fs/sync.c | 1 + include/linux/sched.h | 8 ++++++++ include/linux/task_io_accounting.h | 2 ++ include/linux/task_io_accounting_ops.h | 1 + 4 files changed, 12 insertions(+) diff --git a/fs/sync.c b/fs/sync.c index 1022b89727e5f..4e93b274a2c18 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -205,6 +205,7 @@ static int do_fsync(unsigned int fd, int datasync) if (f.file) { ret = vfs_fsync(f.file, datasync); fdput(f); + inc_syscfs(current); } return ret; } diff --git a/include/linux/sched.h b/include/linux/sched.h index 3bf2401492365..aeb29890c8d57 100755 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -3231,6 +3231,11 @@ static inline void inc_syscw(struct task_struct *tsk) { tsk->ioac.syscw++; } + +static inline void inc_syscfs(struct task_struct *tsk) +{ + tsk->ioac.syscfs++; +} #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { @@ -3247,6 +3252,9 @@ static inline void inc_syscr(struct task_struct *tsk) static inline void inc_syscw(struct task_struct *tsk) { } +static inline void inc_syscfs(struct task_struct *tsk) +{ +} #endif #ifndef TASK_SIZE_OF diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index bdf855c2856fb..2dd338fdf8815 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -18,6 +18,8 @@ struct task_io_accounting { u64 syscr; /* # of write syscalls */ u64 syscw; + /* # of fsync syscalls */ + u64 syscfs; #endif /* CONFIG_TASK_XACCT */ #ifdef CONFIG_TASK_IO_ACCOUNTING diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index 4d090f9ee6087..1b505c804af34 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -96,6 +96,7 @@ static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, dst->wchar += src->wchar; dst->syscr += src->syscr; dst->syscw += src->syscw; + dst->syscfs += src->syscfs; } #else static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, From f32280ab88b6ae8281bf827dd442b7a7666ee644 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 2 Mar 2017 13:39:43 -0800 Subject: [PATCH 147/296] ANDROID: uid_sys_stats: account for fsync syscalls Change-Id: Ie888d8a0f4ec7a27dea86dc4afba8e6fd4203488 Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index bdb440e2fb059..be1cfdca9e133 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -39,6 +39,7 @@ struct io_stats { u64 write_bytes; u64 rchar; u64 wchar; + u64 fsync; }; #define UID_STATE_FOREGROUND 0 @@ -232,6 +233,7 @@ static void add_uid_io_curr_stats(struct uid_entry *uid_entry, io_curr->write_bytes += compute_write_bytes(task); io_curr->rchar += task->ioac.rchar; io_curr->wchar += task->ioac.wchar; + io_curr->fsync += task->ioac.syscfs; } static void clean_uid_io_last_stats(struct uid_entry *uid_entry, @@ -243,6 +245,7 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, io_last->write_bytes -= compute_write_bytes(task); io_last->rchar -= task->ioac.rchar; io_last->wchar -= task->ioac.wchar; + io_last->fsync -= task->ioac.syscfs; } static void update_io_stats_locked(void) @@ -279,11 +282,13 @@ static void update_io_stats_locked(void) io_curr->write_bytes - io_last->write_bytes; io_bucket->rchar += io_curr->rchar - io_last->rchar; io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; io_last->read_bytes = io_curr->read_bytes; io_last->write_bytes = io_curr->write_bytes; io_last->rchar = io_curr->rchar; io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; } } @@ -297,7 +302,7 @@ static int uid_io_show(struct seq_file *m, void *v) update_io_stats_locked(); hash_for_each(hash_table, bkt, uid_entry, hash) { - seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu\n", + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", uid_entry->uid, uid_entry->io[UID_STATE_FOREGROUND].rchar, uid_entry->io[UID_STATE_FOREGROUND].wchar, @@ -306,7 +311,9 @@ static int uid_io_show(struct seq_file *m, void *v) uid_entry->io[UID_STATE_BACKGROUND].rchar, uid_entry->io[UID_STATE_BACKGROUND].wchar, uid_entry->io[UID_STATE_BACKGROUND].read_bytes, - uid_entry->io[UID_STATE_BACKGROUND].write_bytes); + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); } mutex_unlock(&uid_lock); From 1d100c6d84a233c66f50d2a09b2e8da66d0a136f Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 13 Mar 2017 12:22:21 -0700 Subject: [PATCH 148/296] ANDROID: uid_sys_stats: change to use rt_mutex We see this happens multiple times in heavy workload in systrace and AMS stuck in uid_lock. Running process: Process 953 Running thread: android.ui State: Uninterruptible Sleep Start: 1,025.628 ms Duration: 27,955.949 ms On CPU: Running instead: system_server Args: {kernel callsite when blocked:: "uid_procstat_write+0xb8/0x144"} Changing to rt_mutex can mitigate the priority inversion Bug: 34991231 Bug: 34193533 Change-Id: I481baad840b7bc2dfa9b9a59b4dff93cafb90077 Test: on marlin Signed-off-by: Wei Wang Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index be1cfdca9e133..b0a324be8bb2b 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,7 @@ #define UID_HASH_BITS 10 DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); -static DEFINE_MUTEX(uid_lock); +static DEFINE_RT_MUTEX(uid_lock); static struct proc_dir_entry *cpu_parent; static struct proc_dir_entry *io_parent; static struct proc_dir_entry *proc_parent; @@ -100,7 +101,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) cputime_t stime; unsigned long bkt; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); hash_for_each(hash_table, bkt, uid_entry, hash) { uid_entry->active_stime = 0; @@ -114,7 +115,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) current_user_ns(), task_uid(task))); if (!uid_entry) { read_unlock(&tasklist_lock); - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", __func__, from_kuid_munged(current_user_ns(), task_uid(task))); @@ -147,7 +148,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) total_power); } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return 0; } @@ -194,7 +195,7 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { hash_for_each_possible_safe(hash_table, uid_entry, tmp, @@ -206,7 +207,7 @@ static ssize_t uid_remove_write(struct file *file, } } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -255,7 +256,7 @@ static void update_io_stats_locked(void) struct io_stats *io_bucket, *io_curr, *io_last; unsigned long bkt; - BUG_ON(!mutex_is_locked(&uid_lock)); + BUG_ON(!rt_mutex_is_locked(&uid_lock)); hash_for_each(hash_table, bkt, uid_entry, hash) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, @@ -297,7 +298,7 @@ static int uid_io_show(struct seq_file *m, void *v) struct uid_entry *uid_entry; unsigned long bkt; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); update_io_stats_locked(); @@ -316,7 +317,7 @@ static int uid_io_show(struct seq_file *m, void *v) uid_entry->io[UID_STATE_BACKGROUND].fsync); } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return 0; } @@ -361,16 +362,16 @@ static ssize_t uid_procstat_write(struct file *file, if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) return -EINVAL; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); uid_entry = find_or_register_uid(uid); if (!uid_entry) { - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return -EINVAL; } if (uid_entry->state == state) { - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -378,7 +379,7 @@ static ssize_t uid_procstat_write(struct file *file, uid_entry->state = state; - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -400,7 +401,7 @@ static int process_notifier(struct notifier_block *self, if (!task) return NOTIFY_OK; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); uid = from_kuid_munged(current_user_ns(), task_uid(task)); uid_entry = find_or_register_uid(uid); if (!uid_entry) { @@ -418,7 +419,7 @@ static int process_notifier(struct notifier_block *self, clean_uid_io_last_stats(uid_entry, task); exit: - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return NOTIFY_OK; } From 300b05b80873cf8105dd04e5aae061aef67aa2ff Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 14 Mar 2017 16:28:36 -0700 Subject: [PATCH 149/296] ANDROID: uid_sys_stats: fix typo in init Change-Id: I8a41b331c973898015d11d2018257727083f7910 Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- 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 b0a324be8bb2b..ee8e5bc5c2b1d 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -454,7 +454,7 @@ static int __init proc_uid_sys_stats_init(void) &uid_io_fops, NULL); proc_parent = proc_mkdir("uid_procstat", NULL); - if (!io_parent) { + if (!proc_parent) { pr_err("%s: failed to create uid_procstat proc entry\n", __func__); goto err; From 16a4c1186cdc5cb900a68478ef311e32de726431 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 13 Apr 2017 17:07:58 -0700 Subject: [PATCH 150/296] ANDROID: uid_sys_stats: reduce update_io_stats overhead Replaced read_lock with rcu_read_lock to reduce time that preemption is disabled. Added a function to update io stats for specific uid and moved hash table lookup, user_namespace out of loops. Bug: 37319300 Change-Id: I2b81b5cd3b6399b40d08c3c14b42cad044556970 Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 61 ++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index ee8e5bc5c2b1d..094cb9739c079 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -249,28 +249,28 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, io_last->fsync -= task->ioac.syscfs; } -static void update_io_stats_locked(void) +static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; struct task_struct *task, *temp; struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; - - BUG_ON(!rt_mutex_is_locked(&uid_lock)); + uid_t uid; hash_for_each(hash_table, bkt, uid_entry, hash) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; add_uid_io_curr_stats(uid_entry, task); } while_each_thread(temp, task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { io_bucket = &uid_entry->io[uid_entry->state]; @@ -293,6 +293,47 @@ static void update_io_stats_locked(void) } } +static void update_io_stats_uid_locked(uid_t target_uid) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); + + uid_entry = find_or_register_uid(target_uid); + if (!uid_entry) + return; + + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + rcu_read_lock(); + do_each_thread(temp, task) { + if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + rcu_read_unlock(); + + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; +} + static int uid_io_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; @@ -300,7 +341,7 @@ static int uid_io_show(struct seq_file *m, void *v) rt_mutex_lock(&uid_lock); - update_io_stats_locked(); + update_io_stats_all_locked(); hash_for_each(hash_table, bkt, uid_entry, hash) { seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", @@ -375,7 +416,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_locked(); + update_io_stats_uid_locked(uid); uid_entry->state = state; @@ -415,7 +456,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->power += task->cpu_power; task->cpu_power = ULLONG_MAX; - update_io_stats_locked(); + update_io_stats_uid_locked(uid); clean_uid_io_last_stats(uid_entry, task); exit: From d7736f71351458bb9044c7a2b3140ae00353719f Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Tue, 25 Apr 2017 18:07:43 +0800 Subject: [PATCH 151/296] ANDROID: uid_sys_stats: fix access of task_uid(task) struct task_struct *task should be proteced by tasklist_lock. Change-Id: Iefcd13442a9b9d855a2bbcde9fd838a4132fee58 Signed-off-by: Ganesh Mahendran (cherry picked from commit 90d78776c4a0e13fb7ee5bd0787f04a1730631a6) Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 094cb9739c079..e0d123f207e96 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -97,9 +97,11 @@ static int uid_cputime_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; struct task_struct *task, *temp; + struct user_namespace *user_ns = current_user_ns(); cputime_t utime; cputime_t stime; unsigned long bkt; + uid_t uid; rt_mutex_lock(&uid_lock); @@ -111,14 +113,13 @@ static int uid_cputime_show(struct seq_file *m, void *v) read_lock(&tasklist_lock); do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); if (!uid_entry) { read_unlock(&tasklist_lock); rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", - __func__, from_kuid_munged(current_user_ns(), - task_uid(task))); + __func__, uid); return -ENOMEM; } /* if this task is exiting, we have already accounted for the From 2be3a1e0637514fa60a0937165f42bfd3a0b2b0b Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 22 May 2017 12:08:06 -0700 Subject: [PATCH 152/296] ANDROID: uid_sys_stats: defer io stats calulation for dead tasks Store sum of dead task io stats in uid_entry and defer uid io calulation until next uid proc stat change or dumpsys. Bug: 37754877 Change-Id: I970f010a4c841c5ca26d0efc7e027414c3c952e0 Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 107 ++++++++++++++--------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index e0d123f207e96..d4ef4977e1114 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -49,7 +49,8 @@ struct io_stats { #define UID_STATE_TOTAL_CURR 2 #define UID_STATE_TOTAL_LAST 3 -#define UID_STATE_SIZE 4 +#define UID_STATE_DEAD_TASKS 4 +#define UID_STATE_SIZE 5 struct uid_entry { uid_t uid; @@ -226,35 +227,44 @@ static u64 compute_write_bytes(struct task_struct *task) return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; } -static void add_uid_io_curr_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void add_uid_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) { - struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + struct io_stats *io_slot = &uid_entry->io[slot]; - io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += compute_write_bytes(task); - io_curr->rchar += task->ioac.rchar; - io_curr->wchar += task->ioac.wchar; - io_curr->fsync += task->ioac.syscfs; + io_slot->read_bytes += task->ioac.read_bytes; + io_slot->write_bytes += compute_write_bytes(task); + io_slot->rchar += task->ioac.rchar; + io_slot->wchar += task->ioac.wchar; + io_slot->fsync += task->ioac.syscfs; } -static void clean_uid_io_last_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) { - struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; - io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= compute_write_bytes(task); - io_last->rchar -= task->ioac.rchar; - io_last->wchar -= task->ioac.wchar; - io_last->fsync -= task->ioac.syscfs; + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); } static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; uid_t uid; @@ -269,70 +279,38 @@ static void update_io_stats_all_locked(void) uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } } -static void update_io_stats_uid_locked(uid_t target_uid) +static void update_io_stats_uid_locked(struct uid_entry *uid_entry) { - struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); - uid_entry = find_or_register_uid(target_uid); - if (!uid_entry) - return; - memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); rcu_read_lock(); do_each_thread(temp, task) { - if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } static int uid_io_show(struct seq_file *m, void *v) @@ -417,7 +395,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_uid_locked(uid); + update_io_stats_uid_locked(uid_entry); uid_entry->state = state; @@ -457,8 +435,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->power += task->cpu_power; task->cpu_power = ULLONG_MAX; - update_io_stats_uid_locked(uid); - clean_uid_io_last_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS); exit: rt_mutex_unlock(&uid_lock); From 5bb369e994031e64ad5d729384342fd5591ab95f Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 8 Aug 2017 12:02:13 -0700 Subject: [PATCH 153/296] uid_sys_stats: fix overflow when io usage delta is negative Setuid can cause negative delta. Check this and update total usage only if delta is positive. Bug: 64317562 Change-Id: I4818c246db66cabf3b11d277faceedec1678694a Signed-off-by: Jin Qian Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index d4ef4977e1114..3c031f9b0cdb2 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -244,13 +244,29 @@ static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, struct io_stats *io_last, struct io_stats *io_dead) { - io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - + s64 delta; + + delta = io_curr->read_bytes + io_dead->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - + if (delta > 0) + io_bucket->read_bytes += delta; + + delta = io_curr->write_bytes + io_dead->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; + if (delta > 0) + io_bucket->write_bytes += delta; + + delta = io_curr->rchar + io_dead->rchar - io_last->rchar; + if (delta > 0) + io_bucket->rchar += delta; + + delta = io_curr->wchar + io_dead->wchar - io_last->wchar; + if (delta > 0) + io_bucket->wchar += delta; + + delta = io_curr->fsync + io_dead->fsync - io_last->fsync; + if (delta > 0) + io_bucket->fsync += delta; io_last->read_bytes = io_curr->read_bytes; io_last->write_bytes = io_curr->write_bytes; From a9ab9678074e9ce2af7ba6cd8a53e782960db564 Mon Sep 17 00:00:00 2001 From: Artem Borisov Date: Sat, 13 Jan 2018 18:03:40 +0300 Subject: [PATCH 154/296] ANDROID: uid_sys_stats: fix the comment It is not uid_cputime.c anymore. Change-Id: I7effc2a449c1f9cba9d86a7b122a9c05fc266405 Signed-off-by: Artem Borisov Signed-off-by: anupritaisno1 --- 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 3c031f9b0cdb2..2aa7246700b16 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. * From f8573ce40f7f8128ce589a51db516bf656aed6ed Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 11 Apr 2017 14:40:31 +0530 Subject: [PATCH 155/296] ANDROID: Skip building uid_sys_stats and keyreset drivers as modules Few Android drivers viz. uid_sys_stats and keyreset/combo fail to build as kernel modules. uid_sys_stats.ko failed for undefined "tasklist_lock", which got un-exported in commit c59923a15 ("remove the tasklist_lock export"). Quoting from the commit, "Modules have no business looking at it, and all instances in drivers have been due to use of too-lowlevel APIs. Having this symbol exported prevents moving to more scalable locking schemes for the task list.". So instead of exporting tasklist_lock again, lets not build uid_sys_stats driver as module. Similarly skip building keyreset driver as module which call sys_sync() syscall. To keep things in perspective we don't build these drivers as modules in later kernels (android-4.4/4.9) as well. Change-Id: I6371df72d79c7ad0f0c08e6ebf7e16f1b0970761 Signed-off-by: Amit Pundir Signed-off-by: anupritaisno1 --- drivers/input/Kconfig | 5 +---- drivers/misc/Kconfig | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index bb469e32c6082..e16f43657689b 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -175,15 +175,12 @@ config INPUT_APMPOWER module will be called apm-power. config INPUT_KEYRESET - tristate "Reset key" + bool "Reset key" depends on INPUT select INPUT_KEYCOMBO ---help--- Say Y here if you want to reboot when some keys are pressed; - To compile this driver as a module, choose M here: the - module will be called keyreset. - config INPUT_KEYCOMBO tristate "Key combo" depends on INPUT diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 76ef801ea5e00..c33115e2771cd 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -542,7 +542,7 @@ config SRAM areas found on many SoCs. config UID_SYS_STATS - tristate "Per-UID statistics" + bool "Per-UID statistics" depends on PROFILING help Per UID based cpu time statistics exported to /proc/uid_cputime From 28646f5cde67ca020e0e7d15d13e70fa3ba1b330 Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Wed, 24 May 2017 10:28:27 +0800 Subject: [PATCH 156/296] 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 Signed-off-by: anupritaisno1 --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c33115e2771cd..88b808f346416 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -543,7 +543,7 @@ config SRAM 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 From 0657d7f351525d7085e304bb3d47d1e446cfb7a0 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 9 Feb 2016 12:31:42 -0800 Subject: [PATCH 157/296] android: recommended.cfg: enable taskstats CONFIG_TASKSTATS=y CONFIG_TASK_DELAY_ACCT=y CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y Signed-off-by: Mark Salyzyn Bug: 21334988 Bug: 26966375 Change-Id: I17b097ae4ea6c63c2e9fddd9544e3f06d49b609d Signed-off-by: anupritaisno1 --- android/configs/android-recommended.cfg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index 960b9de2860d2..4dd3b4585033d 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -109,6 +109,10 @@ 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 From fcde433f51cbf28f58130ea681ea416698bb7c89 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Thu, 4 Jan 2018 21:04:31 +0800 Subject: [PATCH 158/296] scsi: libsas: fix memory leak in sas_smp_get_phy_events()CVE-2018-7757 We've got a memory leak with the following producer: while true; do cat /sys/class/sas_phy/phy-1:0:12/invalid_dword_count >/dev/null; done The buffer req is allocated and not freed after we return. Fix it. Fixes: 2908d778ab3e ("[SCSI] aic94xx: new driver") Signed-off-by: Jason Yan CC: John Garry CC: chenqilin CC: chenxiang Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: anupritaisno1 --- drivers/scsi/libsas/sas_expander.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 446b85110a1fc..09625081b56c0 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -684,6 +684,7 @@ int sas_smp_get_phy_events(struct sas_phy *phy) phy->phy_reset_problem_count = scsi_to_u32(&resp[24]); out: + kfree(req); kfree(resp); return res; From 012b626a5c9dac12ccf1ebb885236ac396644b9f Mon Sep 17 00:00:00 2001 From: BobZhome Date: Sun, 1 Apr 2018 10:38:21 -0400 Subject: [PATCH 159/296] [PATCH] floppy: Do not copy a kernel pointer to user memory in FDGETPRM ioctl CVE-2018-7755 FromBrian Belleville Date Wed, 7 Mar 2018 16:02:45 -0800 The final field of a floppy_struct is the field name, which is a pointer to a string in kernel memory. The kernel pointer should not be copied to user memory. The FDGETPRM ioctl copies a floppy_struct to user memory, including the name field. This pointer cannot be used by the user, and it will leak a kernel address to user-space, which will reveal the location of kernel code and data and undermine KASLR protection. Instead, copy the floppy_struct except for the name field. Signed-off-by: Brian Belleville Signed-off-by: anupritaisno1 --- drivers/block/floppy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index eb3575b3fbf08..08a8f1217c22b 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3445,6 +3445,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int (struct floppy_struct **)&outparam); if (ret) return ret; + size = offsetof(struct floppy_struct, name); break; case FDMSGON: UDP->flags |= FTD_MSG; From 55b020e66a75533f94950bce885ea78b10e135eb Mon Sep 17 00:00:00 2001 From: BobZhome Date: Sun, 1 Apr 2018 10:56:45 -0400 Subject: [PATCH 160/296] [PATCH] floppy: Don't print kernel addresses to log in show_floppy CVE-2018-7273 From Brian Belleville Date Tue, 20 Feb 2018 14:54:25 -0800 Outputting kernel addresses will reveal the locations of kernel code and data. Change the cases in show_floppy that print fd_timer.work.func and fd_timeout.work.func to use the %pf format specifier, which will print the symbol name, like what is done for the other function pointers printed by show_floppy. No longer output the value of cont. The variable cont is a pointer that can hold the address of kernel global variables. Signed-off-by: Brian Belleville https://lkml.org/lkml/2018/2/20/669 Signed-off-by: anupritaisno1 --- drivers/block/floppy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 08a8f1217c22b..33788cb1ffc64 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -1796,11 +1796,11 @@ static void show_floppy(void) if (work_pending(&floppy_work)) pr_info("floppy_work.func=%pf\n", floppy_work.func); if (delayed_work_pending(&fd_timer)) - pr_info("delayed work.function=%p expires=%ld\n", + pr_info("delayed work.function=%pf expires=%ld\n", fd_timer.work.func, fd_timer.timer.expires - jiffies); if (delayed_work_pending(&fd_timeout)) - pr_info("timer_function=%p expires=%ld\n", + pr_info("timer_function=%pf expires=%ld\n", fd_timeout.work.func, fd_timeout.timer.expires - jiffies); From 9eb71954317c8608e08e7b34d7cfa8d9827e4466 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 19 Feb 2018 01:24:15 +0100 Subject: [PATCH 161/296] netfilter: ebtables: CONFIG_COMPAT: don't trust userland offsets CVE-2018-1068 We need to make sure the offsets are not out of range of the total size. Also check that they are in ascending order. The WARN_ON triggered by syzkaller (it sets panic_on_warn) is changed to also bail out, no point in continuing parsing. Briefly tested with simple ruleset of -A INPUT --limit 1/s' --log plus jump to custom chains using 32bit ebtables binary. Reported-by: Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: anupritaisno1 --- net/bridge/netfilter/ebtables.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 6651a7797d46a..924af39089ead 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -2010,7 +2010,9 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, if (match_kern) match_kern->match_size = ret; - WARN_ON(type == EBT_COMPAT_TARGET && size_left); + if (WARN_ON(type == EBT_COMPAT_TARGET && size_left)) + return -EINVAL; + match32 = (struct compat_ebt_entry_mwt *) buf; } @@ -2067,6 +2069,15 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, * * offsets are relative to beginning of struct ebt_entry (i.e., 0). */ + for (i = 0; i < 4 ; ++i) { + if (offsets[i] >= *total) + return -EINVAL; + if (i == 0) + continue; + if (offsets[i-1] > offsets[i]) + return -EINVAL; + } + for (i = 0, j = 1 ; j < 4 ; j++, i++) { struct compat_ebt_entry_mwt *match32; unsigned int size; From a8326536246117cbf71f9e781cf762f18fd03597 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Jul 2017 12:34:40 +0200 Subject: [PATCH 162/296] ALSA: msnd: Optimize / harden DSP and MIDI loops CVE-2017-9985 The ISA msnd drivers have loops fetching the ring-buffer head, tail and size values inside the loops. Such codes are inefficient and fragile. This patch optimizes it, and also adds the sanity check to avoid the endless loops. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=196131 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=196133 Signed-off-by: Takashi Iwai Signed-off-by: anupritaisno1 --- sound/isa/msnd/msnd_midi.c | 30 +++++++++++++++--------------- sound/isa/msnd/msnd_pinnacle.c | 23 ++++++++++++----------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c index ffc67fd80c23d..58e59cd3c95c0 100644 --- a/sound/isa/msnd/msnd_midi.c +++ b/sound/isa/msnd/msnd_midi.c @@ -120,24 +120,24 @@ void snd_msndmidi_input_read(void *mpuv) unsigned long flags; struct snd_msndmidi *mpu = mpuv; void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF; + u16 head, tail, size; spin_lock_irqsave(&mpu->input_lock, flags); - while (readw(mpu->dev->MIDQ + JQS_wTail) != - readw(mpu->dev->MIDQ + JQS_wHead)) { - u16 wTmp, val; - val = readw(pwMIDQData + 2 * readw(mpu->dev->MIDQ + JQS_wHead)); - - if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, - &mpu->mode)) - snd_rawmidi_receive(mpu->substream_input, - (unsigned char *)&val, 1); - - wTmp = readw(mpu->dev->MIDQ + JQS_wHead) + 1; - if (wTmp > readw(mpu->dev->MIDQ + JQS_wSize)) - writew(0, mpu->dev->MIDQ + JQS_wHead); - else - writew(wTmp, mpu->dev->MIDQ + JQS_wHead); + head = readw(mpu->dev->MIDQ + JQS_wHead); + tail = readw(mpu->dev->MIDQ + JQS_wTail); + size = readw(mpu->dev->MIDQ + JQS_wSize); + if (head > size || tail > size) + goto out; + while (head != tail) { + unsigned char val = readw(pwMIDQData + 2 * head); + + if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) + snd_rawmidi_receive(mpu->substream_input, &val, 1); + if (++head > size) + head = 0; + writew(head, mpu->dev->MIDQ + JQS_wHead); } + out: spin_unlock_irqrestore(&mpu->input_lock, flags); } EXPORT_SYMBOL(snd_msndmidi_input_read); diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 3a7946ebbe238..f7659ecbba0d8 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -170,23 +170,24 @@ static irqreturn_t snd_msnd_interrupt(int irq, void *dev_id) { struct snd_msnd *chip = dev_id; void *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF; + u16 head, tail, size; /* Send ack to DSP */ /* inb(chip->io + HP_RXL); */ /* Evaluate queued DSP messages */ - while (readw(chip->DSPQ + JQS_wTail) != readw(chip->DSPQ + JQS_wHead)) { - u16 wTmp; - - snd_msnd_eval_dsp_msg(chip, - readw(pwDSPQData + 2 * readw(chip->DSPQ + JQS_wHead))); - - wTmp = readw(chip->DSPQ + JQS_wHead) + 1; - if (wTmp > readw(chip->DSPQ + JQS_wSize)) - writew(0, chip->DSPQ + JQS_wHead); - else - writew(wTmp, chip->DSPQ + JQS_wHead); + head = readw(chip->DSPQ + JQS_wHead); + tail = readw(chip->DSPQ + JQS_wTail); + size = readw(chip->DSPQ + JQS_wSize); + if (head > size || tail > size) + goto out; + while (head != tail) { + snd_msnd_eval_dsp_msg(chip, readw(pwDSPQData + 2 * head)); + if (++head > size) + head = 0; + writew(head, chip->DSPQ + JQS_wHead); } + out: /* Send ack to DSP */ inb(chip->io + HP_RXL); return IRQ_HANDLED; From 8a22089a108fbc43b11332482e397419a87092fa Mon Sep 17 00:00:00 2001 From: Roberto Pereira Date: Tue, 10 Oct 2017 17:14:48 -0700 Subject: [PATCH 163/296] ANDROID: scsi: Add segment checking in sg_read CVE-2017-13168 Bug: 65023233 Signed-off-by: Roberto Pereira Change-Id: Ib45f402cf304f9b8bf18884738f92b9c3db55573 Signed-off-by: anupritaisno1 --- drivers/scsi/sg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index aadd3b048f314..4bd31b6adedef 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -400,6 +400,9 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) struct sg_header *old_hdr = NULL; int retval = 0; + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) + return -EINVAL; + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; SCSI_LOG_TIMEOUT(3, printk("sg_read: %s, count=%d\n", From 5f7f7b38d04742f85ec385704de3eb533856a3c3 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Tue, 2 May 2017 12:36:48 -0700 Subject: [PATCH 164/296] ASoC: core: Don't assign an out-of-bounds address to rtd_aux When card->num_aux_devs is zero, card->rtd_aux is assigned an out-of-bounds address. This was found by KASan at runtime: ================================================================== BUG: KASAN: slab-out-of-bounds in msm_audrx_init+0xaa8/0xba4 at addr ffffffc14021ec18 Read of size 8 by task kworker/u8:9/264 page:ffffffbdc5008000 count:1 mapcount:0 mapping: (null) index:0x0 flags: 0x4000000000004000(head) page dumped because: kasan: bad access detected CPU: 3 PID: 264 Comm: kworker/u8:9 Tainted: G B W 3.18.31-Sultan #39 Hardware name: Qualcomm Technologies, Inc. MSM 8996 v3.0 + PMI8996 MTP (DT) Workqueue: deferwq deferred_probe_work_func Call trace: [] dump_backtrace+0x0/0x298 [] show_stack+0x14/0x1c [] dump_stack+0x98/0xc0 [] kasan_report+0x3a4/0x4e8 [] __asan_load8+0x24/0x7c [] msm_audrx_init+0xaa8/0xba4 [] snd_soc_register_card+0x10ac/0x1c04 [] msm8996_asoc_machine_probe+0xd9c/0xf1c [] platform_drv_probe+0x50/0xa4 [] driver_probe_device+0x114/0x2e0 [] __device_attach+0x40/0x64 [] bus_for_each_drv+0xac/0xdc [] device_attach+0x94/0xc0 [] bus_probe_device+0x48/0xf0 [] deferred_probe_work_func+0xa0/0xd0 [] process_one_work+0x324/0x50c [] worker_thread+0x4a4/0x624 [] kthread+0x138/0x14c Memory state around the buggy address: ffffffc14021eb00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ffffffc14021eb80: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe >ffffffc14021ec00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ^ ffffffc14021ec80: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ffffffc14021ed00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ================================================================== Change-Id: I5e5cf2f672753c483917142b6ebf1330995b20a5 Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- sound/soc/soc-core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 83b13d6f6096b..5f5b4fb342a48 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3653,7 +3653,11 @@ int snd_soc_register_card(struct snd_soc_card *card) if (card->rtd == NULL) return -ENOMEM; card->num_rtd = 0; - card->rtd_aux = &card->rtd[card->num_links]; + + if (card->num_aux_devs > 0) + card->rtd_aux = &card->rtd[card->num_links]; + else + card->rtd_aux = NULL; for (i = 0; i < card->num_links; i++) card->rtd[i].dai_link = &card->dai_link[i]; From 03685008f75adb1d682278189820e80d160c38e9 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sun, 9 Apr 2017 14:49:48 -0700 Subject: [PATCH 165/296] power: msm-core: Fix mutex not getting unlocked in error path When this copy_to_user() fails, the mutex won't be unlocked. Fix it. Change-Id: Ide7a7ed9ca8a8d33dafd2060a0c24c57e6396ea8 Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- drivers/power/qcom/msm-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c index 1b9872a4150df..1b98a090c36cd 100644 --- a/drivers/power/qcom/msm-core.c +++ b/drivers/power/qcom/msm-core.c @@ -528,7 +528,7 @@ static long msm_core_ioctl(struct file *file, unsigned int cmd, node->sp->voltage, sizeof(uint32_t) * node->sp->num_of_freqs); if (ret) - break; + goto unlock; for (i = 0; i < node->sp->num_of_freqs; i++) { ret = copy_to_user((void __user *)&argp->freq[i], &node->sp->table[i].frequency, From 02ae403ce4daf13911efe82ba95ab7fb4301cd0e Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sat, 4 Mar 2017 13:58:42 -0800 Subject: [PATCH 166/296] cpufreq: interactive: Skip timer when policy->min == policy->max When the minfreq is set to the maxfreq, there is nothing to really do: the CPU is already running at its maxfreq. In this scenario, just do nothing and re-arm the timer. Signed-off-by: Sultanxda This happens when strong boosts get dispatched by our BoostFramework. Change-Id: Ic95e2f52901965b40d07f055bb93a6a614a5d79c Signed-off-by: Alex Naidis Signed-off-by: anupritaisno1 --- drivers/cpufreq/cpufreq_interactive.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index ba888b776d775..a9ec016a57379 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -435,6 +435,8 @@ static void cpufreq_interactive_timer(unsigned long data) return; if (!ppol->governor_enabled) goto exit; + if (ppol->policy->min == ppol->policy->max) + goto rearm; fcpu = cpumask_first(ppol->policy->related_cpus); now = ktime_to_us(ktime_get()); From 2324d3c9ee0014afc461ac0f019eed3273693093 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Tue, 21 Mar 2017 10:12:10 -0700 Subject: [PATCH 167/296] msm: mdss: Don't cache the address of a stack variable in timings init When the panel timings are parsed, a stack-allocated variable is used to store the parsed data; however, this creates two problems. The first problem is that this creates a memory leak since a kstrdup() address is stored into the stack-allocated variable at the end of the mdss_dsi_panel_timing_from_dt() function. The second problem this creates is that the address of the stack-allocated variable is stored into the current_timing struct member (inside mdss_dsi_panel_timing_switch()) for future use in the driver. Since the data that current_timing points to is expected to persist long after init, allocate memory for the timing settings to fix the issues. Change-Id: I2bbc957b229a010c1b5701f2e40e42e65cd88b2d Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_dsi_panel.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c index 69d1644877f2e..bcb44e5b00f2a 100755 --- a/drivers/video/msm/mdss/mdss_dsi_panel.c +++ b/drivers/video/msm/mdss/mdss_dsi_panel.c @@ -2048,19 +2048,24 @@ static int mdss_dsi_panel_parse_display_timings(struct device_node *np, timings_np = of_get_child_by_name(np, "qcom,mdss-dsi-display-timings"); if (!timings_np) { - struct dsi_panel_timing pt; - memset(&pt, 0, sizeof(struct dsi_panel_timing)); + struct dsi_panel_timing *pt; + + pt = kzalloc(sizeof(*pt), GFP_KERNEL); + if (!pt) + return -ENOMEM; /* * display timings node is not available, fallback to reading * timings directly from root node instead */ pr_debug("reading display-timings from panel node\n"); - rc = mdss_dsi_panel_timing_from_dt(np, &pt); + rc = mdss_dsi_panel_timing_from_dt(np, pt); if (!rc) { mdss_dsi_panel_config_res_properties(np, - panel_data->panel_info.sim_panel_mode, &pt); - rc = mdss_dsi_panel_timing_switch(ctrl, &pt.timing); + panel_data->panel_info.sim_panel_mode, pt); + rc = mdss_dsi_panel_timing_switch(ctrl, &pt->timing); + } else { + kfree(pt); } return rc; } From 3b0841ebbabb5846fe792a5d5ddd89decc344002 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 26 May 2016 17:21:05 -0700 Subject: [PATCH 168/296] UPSTREAM: timer: Export destroy_hrtimer_on_stack() hrtimer_init_on_stack() needs a matching call to destroy_hrtimer_on_stack(), so both need to be exported. Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller (cherry picked from commit c08376ac97cb202ec65320f3d90d5c4c5e2adb0b) [astrachan: Fixes i386-allmodconfig build failure in vsoc.ko noticed by 01.org kbuild-all project building kernel/msm] Bug: 70214720 Change-Id: If4d5c466255019322ea21ef38ee5b1b382cce969 Signed-off-by: Alistair Strachan --- kernel/hrtimer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index bbd1dee81fdd8..116b2276032cc 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -480,6 +480,7 @@ void destroy_hrtimer_on_stack(struct hrtimer *timer) { debug_object_free(timer, &hrtimer_debug_descr); } +EXPORT_SYMBOL_GPL(destroy_hrtimer_on_stack); #else static inline void debug_hrtimer_init(struct hrtimer *timer) { } From 658f4b70c6c135fd4618ad47be7a0d9fa2cdf1f9 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 24 Apr 2018 18:06:56 -0700 Subject: [PATCH 169/296] ANDROID: sdcardfs: Don't d_drop in d_revalidate After d_revalidate returns 0, the vfs will call d_invalidate, which will call d_drop itself, along with other cleanup. Bug: 78262592 Change-Id: Idbb30e008c05d62edf2217679cb6a5517d8d1a2c Signed-off-by: Daniel Rosenberg Signed-off-by: anupritaisno1 --- fs/sdcardfs/dentry.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 8a35955534ab3..e33707897cbcc 100755 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -51,7 +51,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) * whether the base obbpath has been changed or not */ if (is_obbpath_invalid(dentry)) { - d_drop(dentry); return 0; } @@ -65,7 +64,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) { err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); if (err == 0) { - d_drop(dentry); goto out; } } @@ -73,14 +71,12 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_lock(&lower_dentry->d_lock); if (d_unhashed(lower_dentry)) { spin_unlock(&lower_dentry->d_lock); - d_drop(dentry); err = 0; goto out; } spin_unlock(&lower_dentry->d_lock); if (parent_lower_dentry != lower_cur_parent_dentry) { - d_drop(dentry); err = 0; goto out; } @@ -94,7 +90,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) } if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { - __d_drop(dentry); err = 0; } @@ -113,7 +108,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if (inode) { data = top_data_get(SDCARDFS_I(inode)); if (!data || data->abandoned) { - d_drop(dentry); err = 0; } if (data) From da73c0e9f791354364342dac9e5e08e4321e5282 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 17 Nov 2016 11:24:20 -0800 Subject: [PATCH 170/296] UPSTREAM: dm bufio: avoid sleeping while holding the dm_bufio lock We've seen in-field reports showing _lots_ (18 in one case, 41 in another) of tasks all sitting there blocked on: mutex_lock+0x4c/0x68 dm_bufio_shrink_count+0x38/0x78 shrink_slab.part.54.constprop.65+0x100/0x464 shrink_zone+0xa8/0x198 In the two cases analyzed, we see one task that looks like this: Workqueue: kverityd verity_prefetch_io __switch_to+0x9c/0xa8 __schedule+0x440/0x6d8 schedule+0x94/0xb4 schedule_timeout+0x204/0x27c schedule_timeout_uninterruptible+0x44/0x50 wait_iff_congested+0x9c/0x1f0 shrink_inactive_list+0x3a0/0x4cc shrink_lruvec+0x418/0x5cc shrink_zone+0x88/0x198 try_to_free_pages+0x51c/0x588 __alloc_pages_nodemask+0x648/0xa88 __get_free_pages+0x34/0x7c alloc_buffer+0xa4/0x144 __bufio_new+0x84/0x278 dm_bufio_prefetch+0x9c/0x154 verity_prefetch_io+0xe8/0x10c process_one_work+0x240/0x424 worker_thread+0x2fc/0x424 kthread+0x10c/0x114 ...and that looks to be the one holding the mutex. The problem has been reproduced on fairly easily: 0. Be running Chrome OS w/ verity enabled on the root filesystem 1. Pick test patch: http://crosreview.com/412360 2. Install launchBalloons.sh and balloon.arm from http://crbug.com/468342 ...that's just a memory stress test app. 3. On a 4GB rk3399 machine, run nice ./launchBalloons.sh 4 900 100000 ...that tries to eat 4 * 900 MB of memory and keep accessing. 4. Login to the Chrome web browser and restore many tabs With that, I've seen printouts like: DOUG: long bufio 90758 ms ...and stack trace always show's we're in dm_bufio_prefetch(). The problem is that we try to allocate memory with GFP_NOIO while we're holding the dm_bufio lock. Instead we should be using GFP_NOWAIT. Using GFP_NOIO can cause us to sleep while holding the lock and that causes the above problems. The current behavior explained by David Rientjes: It will still try reclaim initially because __GFP_WAIT (or __GFP_KSWAPD_RECLAIM) is set by GFP_NOIO. This is the cause of contention on dm_bufio_lock() that the thread holds. You want to pass GFP_NOWAIT instead of GFP_NOIO to alloc_buffer() when holding a mutex that can be contended by a concurrent slab shrinker (if count_objects didn't use a trylock, this pattern would trivially deadlock). This change significantly increases responsiveness of the system while in this state. It makes a real difference because it unblocks kswapd. In the bug report analyzed, kswapd was hung: kswapd0 D ffffffc000204fd8 0 72 2 0x00000000 Call trace: [] __switch_to+0x9c/0xa8 [] __schedule+0x440/0x6d8 [] schedule+0x94/0xb4 [] schedule_preempt_disabled+0x28/0x44 [] __mutex_lock_slowpath+0x120/0x1ac [] mutex_lock+0x4c/0x68 [] dm_bufio_shrink_count+0x38/0x78 [] shrink_slab.part.54.constprop.65+0x100/0x464 [] shrink_zone+0xa8/0x198 [] balance_pgdat+0x328/0x508 [] kswapd+0x424/0x51c [] kthread+0x10c/0x114 [] ret_from_fork+0x10/0x40 By unblocking kswapd memory pressure should be reduced. Change-Id: I424022893c4934af71aaf52af00f90b17bca2561 Suggested-by: David Rientjes Reviewed-by: Guenter Roeck Signed-off-by: Douglas Anderson Signed-off-by: Mike Snitzer (cherry picked from commit 9ea61cac0b1ad0c09022f39fd97e9b99a2cfc2dc) Signed-off-by: Minchan Kim Signed-off-by: anupritaisno1 --- drivers/md/dm-bufio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 38eca8c634eab..775a150a8b4f9 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -756,7 +756,8 @@ static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client * dm-bufio is resistant to allocation failures (it just keeps * one buffer reserved in cases all the allocations fail). * So set flags to not try too hard: - * GFP_NOIO: don't recurse into the I/O layer + * GFP_NOWAIT: don't wait; if we need to sleep we'll release our + * mutex and wait ourselves. * __GFP_NORETRY: don't retry and rather return failure * __GFP_NOMEMALLOC: don't use emergency reserves * __GFP_NOWARN: don't print a warning in case of failure @@ -766,7 +767,7 @@ static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client */ while (1) { if (dm_bufio_cache_size_latch != 1) { - b = alloc_buffer(c, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); + b = alloc_buffer(c, GFP_NOWAIT | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); if (b) return b; } From f056f657bd837175898d27db394267a72b1a85a3 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 11 Jun 2014 18:19:28 +0200 Subject: [PATCH 171/296] ktime: add ktime_after and ktime_before helper Add two minimal helper functions analogous to time_before() and time_after() that will later on both be needed by SCTP code. Change-Id: Ie6f609e8fbf7b28cd137a19d14504525eec321a6 Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller Git-commit: 67cb9366ff5f99868100198efba5ca88aaa6ad25 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Sahitya Tummala Signed-off-by: anupritaisno1 --- include/linux/ktime.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/linux/ktime.h b/include/linux/ktime.h index e6aacdbebafda..e48fb29c349b7 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -317,6 +317,30 @@ static inline u64 ktime_divns(const ktime_t kt, s64 div) # define ktime_divns(kt, div) (u64)((kt).tv64 / (div)) #endif +/** + * ktime_after - Compare if a ktime_t value is bigger than another one. + * @cmp1: comparable1 + * @cmp2: comparable2 + * + * Return: true if cmp1 happened after cmp2. + */ +static inline bool ktime_after(const ktime_t cmp1, const ktime_t cmp2) +{ + return ktime_compare(cmp1, cmp2) > 0; +} + +/** + * ktime_before - Compare if a ktime_t value is smaller than another one. + * @cmp1: comparable1 + * @cmp2: comparable2 + * + * Return: true if cmp1 happened before cmp2. + */ +static inline bool ktime_before(const ktime_t cmp1, const ktime_t cmp2) +{ + return ktime_compare(cmp1, cmp2) < 0; +} + static inline s64 ktime_to_us(const ktime_t kt) { return ktime_divns(kt, NSEC_PER_USEC); From 100c47761702386f0a3263b0850950cec0d408ab Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 16 Jul 2014 21:03:56 +0000 Subject: [PATCH 172/296] ktime: Change ktime_set() to take 64bit seconds value In order to support dates past 2038 on 32bit systems, ktime_set() needs to handle 64bit second values. [ tglx: Removed the BITS_PER_LONG check ] Change-Id: I59037ff2d8b536aeda51061fd85601a596821154 Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz Git-commit: b17b20d70dcbe48dd1aa6aba073a60ddfce5d7db Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Sahitya Tummala Signed-off-by: anupritaisno1 --- include/linux/ktime.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/linux/ktime.h b/include/linux/ktime.h index e48fb29c349b7..638794b816ef2 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -71,13 +71,12 @@ typedef union ktime ktime_t; /* Kill this */ * * Return the ktime_t representation of the value */ -static inline ktime_t ktime_set(const long secs, const unsigned long nsecs) +static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs) { -#if (BITS_PER_LONG == 64) if (unlikely(secs >= KTIME_SEC_MAX)) return (ktime_t){ .tv64 = KTIME_MAX }; -#endif - return (ktime_t) { .tv64 = (s64)secs * NSEC_PER_SEC + (s64)nsecs }; + + return (ktime_t) { .tv64 = secs * NSEC_PER_SEC + (s64)nsecs }; } /* Subtract two ktime_t variables. rem = lhs -rhs: */ From 342a61e55f18e5ad90bf848794859b773cf7942c Mon Sep 17 00:00:00 2001 From: Bala Venkatesh Date: Wed, 18 Oct 2017 13:08:38 +0530 Subject: [PATCH 173/296] qcacld-2.0: Check vdev_id against wma->max_bssid Check vdev_id against wma->max_bssid in wma_mcc_vdev_tx_pause_evt_handler to avoid bufer overflow. Bug: 70237692 Change-Id: Ie47a0ed2f7f27f13a01e1b2cb365fae66b41b1df CRs-Fixed: 2119404 Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 0d634a767da18..069a4bef66d0a 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -31905,7 +31905,8 @@ static int wma_mcc_vdev_tx_pause_evt_handler(void *handle, u_int8_t *event, /* FW mapped vdev from ID * vdev_map = (1 << vdev_id) * So, host should unmap to ID */ - for (vdev_id = 0; vdev_map != 0; vdev_id++) + for (vdev_id = 0; vdev_map != 0 && vdev_id < wma->max_bssid; + vdev_id++) { if (!(vdev_map & 0x1)) { From 587b3d0677c86333e416d3a6104ed49239545b0c Mon Sep 17 00:00:00 2001 From: gaurank kathpalia Date: Thu, 30 Nov 2017 17:28:22 +0530 Subject: [PATCH 174/296] qcacld-2.0: Avoid possible buffer overwrite in wma_process_utf_event Check for the maximum allowed data that can be written into the buffer utf_event_info.data in the function wma_process_utf_event. Bug: 68992451 Change-Id: I9ee37470b7a3e7016941f871d3cf73eb12718758 CRs-Fixed: 2131653 Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 069a4bef66d0a..6dcd0225e6651 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -35109,6 +35109,14 @@ wma_process_utf_event(WMA_HANDLE handle, currentSeq); } + if ((datalen > MAX_UTF_EVENT_LENGTH) || + (wma_handle->utf_event_info.offset > + (MAX_UTF_EVENT_LENGTH - datalen))) { + WMA_LOGE("Excess data from firmware, offset:%zu, len:%d", + wma_handle->utf_event_info.offset, datalen); + return -EINVAL; + } + memcpy(&wma_handle->utf_event_info.data[wma_handle->utf_event_info.offset], &data[sizeof(segHdrInfo)], datalen); From 536343244f0c3847c4c942979a0d3b75b7b87e72 Mon Sep 17 00:00:00 2001 From: gaurank kathpalia Date: Tue, 3 Oct 2017 18:13:49 +0530 Subject: [PATCH 175/296] qcacld-2.0: Check for valid vdev ID in wma_nlo_match_evt_handler Check if the firmware is passing a valid vdev ID or not in the NLO match event and return error if vdev is invalid Bug: 68992442 Change-Id: I83f957ae084e17c20f607eb3862a131f3b311d23 CRs-Fixed: 2132377 Signed-off-by: Ecco Park Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 6dcd0225e6651..95fd2dc6bbf5d 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -31757,6 +31757,11 @@ static int wma_nlo_match_evt_handler(void *handle, u_int8_t *event, nlo_event = param_buf->fixed_param; WMA_LOGD("PNO match event received for vdev %d", nlo_event->vdev_id); + if (nlo_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("Invalid vdev id in the NLO event %d", + nlo_event->vdev_id); + return -EINVAL; + } node = &wma->interfaces[nlo_event->vdev_id]; if (node) From 8e28f0a42306e8dce6553afc8a2ce7c66989ede9 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 31 Oct 2017 17:02:13 +0530 Subject: [PATCH 176/296] qcacld-2.0: Check for the max number of P2P NOA descriptors Check for the maximum number of P2P NOA descriptors in wma_send_bcn_buf_ll. Bug: 68992438 Change-Id: If7e5b3c53309412dc7d3cd748c2f5581898fbbfe CRs-Fixed: 2135600 Signed-off-by: Ahmed ElArabawy Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 95fd2dc6bbf5d..1d134f175ddd6 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -5342,6 +5342,12 @@ static void wma_send_bcn_buf_ll(tp_wma_handle wma, WMA_LOGE("%s: Invalid beacon buffer", __func__); return; } + if (WMI_UNIFIED_NOA_ATTR_NUM_DESC_GET(p2p_noa_info) > + WMI_P2P_MAX_NOA_DESCRIPTORS) { + WMA_LOGE("%s: Too many descriptors %d", __func__, + WMI_UNIFIED_NOA_ATTR_NUM_DESC_GET(p2p_noa_info)); + return; + } wmi_buf = wmi_buf_alloc(wma->wmi_handle, sizeof(*cmd)); if (!wmi_buf) { From 2b3043c6b864d4912bc96d90f914ee1d74b4dbe9 Mon Sep 17 00:00:00 2001 From: gaurank kathpalia Date: Thu, 28 Sep 2017 16:41:57 +0530 Subject: [PATCH 177/296] qcacld-2.0: Check for upper bound in P2P NOA event Check for the upper bounds for number of NOA descriptors received in the P2P NOA event. Bug: 68992432 Change-Id: Id7ecf064f2c25f378f76d795902713da8520507f CRs-Fixed: 2132226 Signed-off-by: Ecco Park Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 1d134f175ddd6..4b2ab8144da18 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -5974,6 +5974,12 @@ static int wma_p2p_noa_event_handler(void *handle, u_int8_t *event, u_int32_t le descriptors = WMI_UNIFIED_NOA_ATTR_NUM_DESC_GET(p2p_noa_info); noa_ie.num_descriptors = (u_int8_t)descriptors; + if (noa_ie.num_descriptors > WMA_MAX_NOA_DESCRIPTORS) { + WMA_LOGD("Sizing down the no of desc %d to max", + noa_ie.num_descriptors); + noa_ie.num_descriptors = WMA_MAX_NOA_DESCRIPTORS; + } + WMA_LOGI("%s: index %u, oppPs %u, ctwindow %u, " "num_descriptors = %u", __func__, noa_ie.index, noa_ie.oppPS, noa_ie.ctwindow, noa_ie.num_descriptors); From e08f38f75e6d6cbfc3255d08dfa7be25b9c74a52 Mon Sep 17 00:00:00 2001 From: Srinivas Girigowda Date: Mon, 2 Oct 2017 14:02:29 -0700 Subject: [PATCH 178/296] qcacld-2.0: Set length of challenge text sent by SAP to 128 SIR_MAC_AUTH_CHALLENGE_LENGTH is updated to 253 from 128 as per IEEE spec due to connection fails between DUT-SAP and old ref-STA. Auth failure occurs as encrypted data sent by ref-STA is only 128 bytes instead of 253 bytes. Fix is to set length of challenge text sent by SAP to 128 bytes. Change-Id: I20eda5ffc0cca4dc4b64beece0740932e13eacb8 CRs-Fixed: 2103899 Bug: 67030205 Bug: 68992395 Signed-off-by: Srinivas Girigowda Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/MAC/inc/sirMacProtDef.h | 1 + .../CORE/MAC/src/pe/lim/limProcessAuthFrame.c | 13 +++++++------ .../CORE/MAC/src/pe/lim/limSecurityUtils.h | 5 +++++ .../CORE/MAC/src/pe/lim/limSendManagementFrames.c | 7 ++++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirMacProtDef.h b/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirMacProtDef.h index 30bb3ca957ecc..ab5487456b62c 100644 --- a/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirMacProtDef.h +++ b/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirMacProtDef.h @@ -586,6 +586,7 @@ #define SIR_MAC_MAX_NUM_OF_DEFAULT_KEYS 4 #define SIR_MAC_KEY_LENGTH 13 // WEP Maximum key length size #define SIR_MAC_AUTH_CHALLENGE_LENGTH 253 +#define SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH 128 #define SIR_MAC_WEP_IV_LENGTH 4 #define SIR_MAC_WEP_ICV_LENGTH 4 #define SIR_MAC_CHALLENGE_ID_LEN 2 diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c index 05591d0494441..7649251346384 100644 --- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c +++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c @@ -290,7 +290,7 @@ limProcessAuthFrame(tpAniSirGlobal pMac, tANI_U8 *pRxPacketInfo, tpPESession pse goto free; } - if (frameLen < LIM_ENCR_AUTH_BODY_LEN) + if (frameLen < LIM_ENCR_AUTH_BODY_LEN_SAP) { // Log error limLog(pMac, LOGE, @@ -966,9 +966,9 @@ limProcessAuthFrame(tpAniSirGlobal pMac, tANI_U8 *pRxPacketInfo, tpPESession pse pAuthNode->fTimerStarted = 1; /* - * get random bytes and use as challenge text. + * get random bytes and use as challenge text */ - if( !VOS_IS_STATUS_SUCCESS( vos_rand_get_bytes( 0, (tANI_U8 *)challengeTextArray, SIR_MAC_AUTH_CHALLENGE_LENGTH ) ) ) + if( !VOS_IS_STATUS_SUCCESS( vos_rand_get_bytes( 0, (tANI_U8 *)challengeTextArray, SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH ) ) ) { limLog(pMac, LOGE,FL("Challenge text preparation failed in limProcessAuthFrame")); goto free; @@ -990,11 +990,12 @@ limProcessAuthFrame(tpAniSirGlobal pMac, tANI_U8 *pRxPacketInfo, tpPESession pse pRxAuthFrameBody->authTransactionSeqNumber + 1; authFrame->authStatusCode = eSIR_MAC_SUCCESS_STATUS; + authFrame->type = SIR_MAC_CHALLENGE_TEXT_EID; - authFrame->length = SIR_MAC_AUTH_CHALLENGE_LENGTH; + authFrame->length = SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH; vos_mem_copy(authFrame->challengeText, pAuthNode->challengeText, - SIR_MAC_AUTH_CHALLENGE_LENGTH); + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH); limSendAuthMgmtFrame( pMac, authFrame, @@ -1596,7 +1597,7 @@ limProcessAuthFrame(tpAniSirGlobal pMac, tANI_U8 *pRxPacketInfo, tpPESession pse if (vos_mem_compare(pRxAuthFrameBody->challengeText, pAuthNode->challengeText, - SIR_MAC_AUTH_CHALLENGE_LENGTH)) + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH)) { /// Challenge match. STA is autheticated ! diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSecurityUtils.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSecurityUtils.h index 4fb961c3cebb7..3dd987b1fa9ad 100644 --- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSecurityUtils.h +++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSecurityUtils.h @@ -48,6 +48,11 @@ SIR_MAC_WEP_ICV_LENGTH + \ SIR_MAC_CHALLENGE_ID_LEN) +#define LIM_ENCR_AUTH_BODY_LEN_SAP (SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH + \ + SIR_MAC_CHALLENGE_ID_LEN + \ + SIR_MAC_AUTH_FRAME_INFO_LEN + \ + SIR_MAC_WEP_IV_LENGTH + \ + SIR_MAC_WEP_ICV_LENGTH) struct tLimPreAuthNode; tANI_U8 limIsAuthAlgoSupported(tpAniSirGlobal, tAniAuthType, tpPESession); diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c index ac0def36471c6..162d7ed91acf2 100644 --- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c +++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c @@ -3516,10 +3516,11 @@ limSendAuthMgmtFrame(tpAniSirGlobal pMac, * transaction number, status code and 128 bytes * for challenge text. */ - + bodyLen = SIR_MAC_AUTH_FRAME_INFO_LEN + + SIR_MAC_SAP_AUTH_CHALLENGE_LENGTH + + SIR_MAC_CHALLENGE_ID_LEN; frameLen = sizeof(tSirMacMgmtHdr) + - sizeof(tSirMacAuthFrame); - bodyLen = sizeof(tSirMacAuthFrameBody); + bodyLen; } break; From b444d3eb042ddb2aa040cf07a82fab3643da38ff Mon Sep 17 00:00:00 2001 From: Srinivas Girigowda Date: Fri, 1 Dec 2017 14:57:46 -0800 Subject: [PATCH 179/296] qcacld-2.0: Add sanity check to limit mgmt frames data len Currently the mpdu_data_len in Rx pkt meta is not checked for upper bound in wma_form_rx_packet. Add sanity check to drop the packet if mpdu_data_len is greater than 2000 bytes. Also add upper bound check for frame_len in lim_process_auth_frame function. Change-Id: I387615127ab98ef43baa6f2570b0433af39a016e CRs-Fixed: 2133040 Bug: 68992395 Signed-off-by: Srinivas Girigowda Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/CLD_TXRX/TLSHIM/tl_shim.c | 10 ++++++++++ .../CORE/MAC/src/pe/lim/limProcessAuthFrame.c | 3 ++- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TLSHIM/tl_shim.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TLSHIM/tl_shim.c index 3ebe957c5d794..b09445f59fd45 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TLSHIM/tl_shim.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TLSHIM/tl_shim.c @@ -642,6 +642,16 @@ static int tlshim_mgmt_rx_process(void *context, u_int8_t *data, rx_pkt->pkt_meta.mpdu_data_len = hdr->buf_len - rx_pkt->pkt_meta.mpdu_hdr_len; + /* + * If the mpdu_data_len is greater than Max (2k), drop the frame + */ + if (rx_pkt->pkt_meta.mpdu_data_len > WMA_MAX_MGMT_MPDU_LEN) { + TLSHIM_LOGE("Data Len %d greater than max, dropping frame", + rx_pkt->pkt_meta.mpdu_data_len); + vos_mem_free(rx_pkt); + return 0; + } + /* * saved_beacon means this beacon is a duplicate of one * sent earlier. roamCandidateInd flag is used to indicate to diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c index 7649251346384..af5411e18eaf6 100644 --- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c +++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAuthFrame.c @@ -290,7 +290,8 @@ limProcessAuthFrame(tpAniSirGlobal pMac, tANI_U8 *pRxPacketInfo, tpPESession pse goto free; } - if (frameLen < LIM_ENCR_AUTH_BODY_LEN_SAP) + if ((frameLen < LIM_ENCR_AUTH_BODY_LEN_SAP) || + (frameLen > LIM_ENCR_AUTH_BODY_LEN_SAP)) { // Log error limLog(pMac, LOGE, diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h index f8f2028f79df4..c210ec094fa6b 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h @@ -101,6 +101,8 @@ #define FRAGMENT_SIZE 3072 +#define WMA_MAX_MGMT_MPDU_LEN 2000 + #define WMA_INVALID_VDEV_ID 0xFF #define MAX_MEM_CHUNKS 32 #define WMA_MAX_VDEV_SIZE 20 From 846cf9df5b6d678b5b143d861ec3a6f835457623 Mon Sep 17 00:00:00 2001 From: "Poddar, Siddarth" Date: Wed, 6 Dec 2017 12:44:30 +0530 Subject: [PATCH 180/296] qcacld-2.0: Fix potential buffer overflow in process_tx_info Check for buffer overflow for pktlog messages in process_tx_info function before doing mem copy. Bug: 72957136 Change-Id: Ic8ee17fa03a987468405c9978aa06ee849fa5661 CRs-Fixed: 2154331 Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/UTILS/PKTLOG/pktlog_internal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/pktlog_internal.c b/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/pktlog_internal.c index 3db0b8758af21..79c7dc81915be 100644 --- a/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/pktlog_internal.c +++ b/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/pktlog_internal.c @@ -307,6 +307,10 @@ process_tx_info(struct ol_txrx_pdev_t *txrx_pdev, */ txctl_log.priv.frm_hdr = frm_hdr; adf_os_assert(txctl_log.priv.txdesc_ctl); + adf_os_assert(pl_hdr.size < sizeof(txctl_log.priv.txdesc_ctl)); + pl_hdr.size = (pl_hdr.size > sizeof(txctl_log.priv.txdesc_ctl)) + ? sizeof(txctl_log.priv.txdesc_ctl) : + pl_hdr.size; adf_os_mem_copy((void *)&txctl_log.priv.txdesc_ctl, ((void *)data + sizeof(struct ath_pktlog_hdr)), pl_hdr.size); From 777344b9f8371061df779738042787b607c8e50b Mon Sep 17 00:00:00 2001 From: Tiger Yu Date: Wed, 6 Dec 2017 13:43:46 +0800 Subject: [PATCH 181/296] qcacld-2.0: Fix potential buffer overflow in ol_rx_flush_handler Check for the validity of tid when received the htt message of HTT_T2H_MSG_TYPE_RX_FLUSH & HTT_T2H_MSG_TYPE_RX_PN_IND from firmware to ensure the buffer overflow does not happen. And correct the sequence number type from signed int to unsigned. Bug: 72957235 Change-Id: I1d333acddfcfafcd23d8ba8da676384d28d0a471 CRs-Fixed: 2149399 Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c | 8 ++++---- .../qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.c | 14 +++++++++++--- .../qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.h | 4 ++-- .../qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_reorder.c | 15 ++++++++++++--- .../CORE/SERVICES/COMMON/ol_htt_rx_api.h | 4 ++-- .../CORE/SERVICES/COMMON/ol_txrx_htt_api.h | 4 ++-- 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c index b736a970c5750..6bdc9f628eac1 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c @@ -172,7 +172,7 @@ htt_t2h_lp_msg_handler(void *context, adf_nbuf_t htt_t2h_msg ) { u_int16_t peer_id; u_int8_t tid; - int seq_num_start, seq_num_end; + u_int16_t seq_num_start, seq_num_end; enum htt_rx_flush_action action; peer_id = HTT_RX_FLUSH_PEER_ID_GET(*msg_word); @@ -675,7 +675,7 @@ if (adf_os_unlikely(pdev->rx_ring.rx_reset)) { { u_int16_t peer_id; u_int8_t tid, pn_ie_cnt, *pn_ie=NULL; - int seq_num_start, seq_num_end; + u_int16_t seq_num_start, seq_num_end; /*First dword */ peer_id = HTT_RX_PN_IND_PEER_ID_GET(*msg_word); @@ -1077,8 +1077,8 @@ void htt_rx_frag_ind_flush_seq_num_range( htt_pdev_handle pdev, adf_nbuf_t rx_frag_ind_msg, - int *seq_num_start, - int *seq_num_end) + u_int16_t *seq_num_start, + u_int16_t *seq_num_end) { u_int32_t *msg_word; diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.c index 358d6d660c3bf..420683bf9cf5c 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014, 2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2014, 2016-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -192,12 +192,20 @@ ol_rx_frag_indication_handler( u_int8_t tid) { u_int16_t seq_num; - int seq_num_start, seq_num_end; + u_int16_t seq_num_start, seq_num_end; struct ol_txrx_peer_t *peer; htt_pdev_handle htt_pdev; adf_nbuf_t head_msdu, tail_msdu; void *rx_mpdu_desc; + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, + "%s: invalid tid, %u\n", + __FUNCTION__, + tid); + return; + } + htt_pdev = pdev->htt_pdev; peer = ol_txrx_peer_find_by_id(pdev, peer_id); @@ -245,7 +253,7 @@ ol_rx_reorder_flush_frag( htt_pdev_handle htt_pdev, struct ol_txrx_peer_t *peer, unsigned tid, - int seq_num) + u_int16_t seq_num) { struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; int seq; diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.h index 737c299873057..7750f2456fd68 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.h +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_defrag.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -165,7 +165,7 @@ ol_rx_reorder_flush_frag( htt_pdev_handle htt_pdev, struct ol_txrx_peer_t *peer, unsigned tid, - int seq_num); + u_int16_t seq_num); static inline void xor_block( diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_reorder.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_reorder.c index e1ffff092a92f..05f54907bb509 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_reorder.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx_reorder.c @@ -583,6 +583,14 @@ ol_rx_flush_handler( struct ol_rx_reorder_array_elem_t *rx_reorder_array_elem; htt_pdev_handle htt_pdev = pdev->htt_pdev; + if (tid >= OL_TXRX_NUM_EXT_TIDS) { + TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, + "%s: invalid tid, %u\n", + __FUNCTION__, + tid); + return; + } + peer = ol_txrx_peer_find_by_id(pdev, peer_id); if (peer) { vdev = peer->vdev; @@ -622,8 +630,8 @@ ol_rx_pn_ind_handler( ol_txrx_pdev_handle pdev, u_int16_t peer_id, u_int8_t tid, - int seq_num_start, - int seq_num_end, + u_int16_t seq_num_start, + u_int16_t seq_num_end, u_int8_t pn_ie_cnt, u_int8_t *pn_ie) { @@ -635,7 +643,8 @@ ol_rx_pn_ind_handler( adf_nbuf_t head_msdu = NULL; adf_nbuf_t tail_msdu = NULL; htt_pdev_handle htt_pdev = pdev->htt_pdev; - int seq_num, i=0; + u_int16_t seq_num; + int i=0; peer = ol_txrx_peer_find_by_id(pdev, peer_id); diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_htt_rx_api.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_htt_rx_api.h index b8e98250a4be7..ba043dbdc4e65 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_htt_rx_api.h +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_htt_rx_api.h @@ -833,8 +833,8 @@ void htt_rx_frag_ind_flush_seq_num_range( htt_pdev_handle pdev, adf_nbuf_t rx_frag_ind_msg, - int *seq_num_start, - int *seq_num_end); + u_int16_t *seq_num_start, + u_int16_t *seq_num_end); /** * @brief Return the HL rx desc size * @param pdev - the HTT instance the rx data was received on diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_htt_api.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_htt_api.h index 0baafe0f6bdec..8d42b0f061146 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_htt_api.h +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_htt_api.h @@ -617,8 +617,8 @@ ol_rx_pn_ind_handler( ol_txrx_pdev_handle pdev, u_int16_t peer_id, u_int8_t tid, - int seq_num_start, - int seq_num_end, + u_int16_t seq_num_start, + u_int16_t seq_num_end, u_int8_t pn_ie_cnt, u_int8_t *pn_ie); From a2b4bed87a94ade2d7da3906c76011b470a2eadc Mon Sep 17 00:00:00 2001 From: Tiger Yu Date: Fri, 1 Dec 2017 10:13:56 +0800 Subject: [PATCH 182/296] qcacld-2.0: Fix potential buffer overflow in htt_t2h_lp_msg_handler Check for the validity of peer_id when received the htt message of HTT_T2H_MSG_TYPE_PEER_MAP or HTT_T2H_MSG_TYPE_PEER_UNMAP from firmware to ensure the buffer overflow does not happen. Bug: 72956997 Change-Id: I2c589e9f8a99ba18b7ee629b80732fbc5e2c1f44 CRs-Fixed: 2147119 Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c index 6bdc9f628eac1..f20152b1783d5 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c @@ -297,6 +297,14 @@ htt_t2h_lp_msg_handler(void *context, adf_nbuf_t htt_t2h_msg ) peer_mac_addr = htt_t2h_mac_addr_deswizzle( (u_int8_t *) (msg_word+1), &mac_addr_deswizzle_buf[0]); + if (peer_id > ol_cfg_max_peer_id(pdev->ctrl_pdev)) { + adf_os_print("%s: HTT_T2H_MSG_TYPE_PEER_MAP," + "invalid peer_id, %u\n", + __FUNCTION__, + peer_id); + break; + } + ol_rx_peer_map_handler( pdev->txrx_pdev, peer_id, vdev_id, peer_mac_addr, 1/*can tx*/); break; @@ -306,6 +314,14 @@ htt_t2h_lp_msg_handler(void *context, adf_nbuf_t htt_t2h_msg ) u_int16_t peer_id; peer_id = HTT_RX_PEER_UNMAP_PEER_ID_GET(*msg_word); + if (peer_id > ol_cfg_max_peer_id(pdev->ctrl_pdev)) { + adf_os_print("%s: HTT_T2H_MSG_TYPE_PEER_UNMAP," + "invalid peer_id, %u\n", + __FUNCTION__, + peer_id); + break; + } + ol_rx_peer_unmap_handler(pdev->txrx_pdev, peer_id); break; } From 3e10f776d4e0e8d8a31190d274078f731c86d1ca Mon Sep 17 00:00:00 2001 From: Vignesh Viswanathan Date: Mon, 20 Nov 2017 23:34:12 +0530 Subject: [PATCH 183/296] qcacld-2.0: Fix buffer overrun in function ProcSetReqInternal In function ProcSetReqInternal, valueLen is obtained from the message buffer pParam. This valueLen is used as argument to the function GetStrValue where the contents of the buffer pParam is copied to pMac->cfg.gSBuffer for valueLen number of bytes. However the array pMac->cfg.gSBuffer is a static array of size CFG_MAX_STR_LEN. If the value of valueLen exceeds CFG_MAX_STR_LEN, a buffer overwrite will occur in GetStrValue. Add Sanity check to make sure valueLen does not exceed CFG_MAX_STR_LEN. Bug: 72957177 Change-Id: I9bf3a502d4b73c37e7b4ece963e7ce493274c893 CRs-Fixed: 2143847 Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgProcMsg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgProcMsg.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgProcMsg.c index 943ba48cab560..947f848401406 100644 --- a/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgProcMsg.c +++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgProcMsg.c @@ -2712,7 +2712,8 @@ ProcSetReqInternal(tpAniSirGlobal pMac, tANI_U16 length, tANI_U32 *pParam, tANI_ // Process string parameter else { - if (valueLenRoundedUp4 > length) + if ((valueLenRoundedUp4 > length) || + (valueLen > CFG_MAX_STR_LEN)) { PELOGE(cfgLog(pMac, LOGE, FL("Invalid string length %d" "in set param %d (tot %d)"), valueLen, From 7d24cbf4a29bb1a37a7cb13d77fe1ccc204bb795 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 20 Dec 2017 12:49:54 +0530 Subject: [PATCH 184/296] qcacld-2.0: Fix buffer overread in wma_extscan_hotlist_match_event_handler In function wma_extscan_hotlist_match_event_handler, numap and src_hotlist are received from the FW. src_hotlist is pointer to the hostist data and is looped for numap times and copied to the local buffer dest_hotlist. If the value of numap is not equal to the number of src_hotlist data present in the buffer, buffer overread would occur during memcpy. Add check to validate the len of the buffer received from the FW is not less than the size of fixparam struct + (numap * src_hostlist structure) Bug: 72956920 Change-Id: Idbcb680b64eca399b27f9e7edeccbac21bf8ddfb CRs-Fixed: 2148646 Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 4b2ab8144da18..f19de1c21bde3 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -3929,7 +3929,7 @@ static int wma_extscan_hotlist_match_event_handler(void *handle, wmi_extscan_wlan_descriptor *src_hotlist; uint32_t numap; int j, ap_found = 0; - + uint32_t buf_len; tpAniSirGlobal pMac = (tpAniSirGlobal )vos_get_context( VOS_MODULE_ID_PE, wma->vos_context); if (!pMac) { @@ -3959,6 +3959,13 @@ static int wma_extscan_hotlist_match_event_handler(void *handle, __func__, numap); numap = WMA_EXTSCAN_MAX_HOTLIST_ENTRIES; } + buf_len = sizeof(wmi_extscan_hotlist_match_event_fixed_param) + + (4 * sizeof(uint32_t)) + + (numap * sizeof(wmi_extscan_wlan_descriptor)); + if (buf_len > len) { + WMA_LOGE("Invalid buf len from FW %d numap %d", len, numap); + return -EINVAL; + } dest_hotlist = vos_mem_malloc(sizeof(*dest_hotlist) + sizeof(*dest_ap) * numap); if (!dest_hotlist) { From 41dd1a443634efdbbd4e8e19b0d28f2ccf2f7b61 Mon Sep 17 00:00:00 2001 From: tinlin Date: Thu, 11 Jan 2018 14:46:59 +0800 Subject: [PATCH 185/296] qcacld-2.0: Calculate buf_len properly for extscan hotlist event buffer Calculate buf_len properly for extscan hotlist event buffer in wma_extscan_hotlist_match_event_handler() Bug: 72956920 Change-Id: I2ae9d3b30dad2c6143d8014d655fdcc06b85eb1e CRs-Fixed: 2170578 Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index f19de1c21bde3..847892760d23c 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -3960,7 +3960,7 @@ static int wma_extscan_hotlist_match_event_handler(void *handle, numap = WMA_EXTSCAN_MAX_HOTLIST_ENTRIES; } buf_len = sizeof(wmi_extscan_hotlist_match_event_fixed_param) + - (4 * sizeof(uint32_t)) + + WMI_TLV_HDR_SIZE + (numap * sizeof(wmi_extscan_wlan_descriptor)); if (buf_len > len) { WMA_LOGE("Invalid buf len from FW %d numap %d", len, numap); From dd885467dd0330877940000e7eeb489c68ac4b02 Mon Sep 17 00:00:00 2001 From: Srinivas Girigowda Date: Wed, 14 Feb 2018 15:01:51 -0800 Subject: [PATCH 186/296] qcacld-2.0: Add sanity check for vdev id to prevent OOB access Add sanity check for vdev id in wma_vdev_start_resp_handler() to prevent out of bound memory access. Change-Id: I2a496e3f5b546d20813e7fce208c037f4bf68e42 CRs-Fixed: 2120424 Bug: 71501694 Signed-off-by: Srinivas Girigowda Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 847892760d23c..8c8826a1c0a85 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -1141,6 +1141,11 @@ static int wma_vdev_start_resp_handler(void *handle, u_int8_t *cmd_param_info, return -EINVAL; } + if (resp_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("Invalid vdev id received from firmware"); + return -EINVAL; + } + if (wma_is_vdev_in_ap_mode(wma, resp_event->vdev_id)) { adf_os_spin_lock_bh(&wma->dfs_ic->chan_lock); wma->dfs_ic->disable_phy_err_processing = false; From fa69ae07a3f22b69939b8a71ca62ecce418ed3b1 Mon Sep 17 00:00:00 2001 From: Srinivas Girigowda Date: Wed, 14 Feb 2018 15:34:55 -0800 Subject: [PATCH 187/296] qcacld-2.0: Add sanity check for vdev_id in wma_wow_wakeup_host_event Currently wake_info->vdev_id, recevied from the FW, is directly used to refer to wma->interfaces without validating if the vdev_id is valid. Add sanity check to make sure vdev_id is less than max_bssid before using it. Change-Id: If7612be6c5b3ca4fc541b4168995f58e7f92f3e5 CRs-Fixed: 2114363 Bug: 71501687 Signed-off-by: Srinivas Girigowda Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 8c8826a1c0a85..1b755e01de69e 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -21491,19 +21491,18 @@ static int wma_wow_wakeup_host_event(void *handle, u_int8_t *event, wake_info = param_buf->fixed_param; - if ((wake_info->wake_reason != WOW_REASON_UNSPECIFIED) || - (wake_info->wake_reason == WOW_REASON_UNSPECIFIED && - !wmi_get_runtime_pm_inprogress(wma->wmi_handle))) { + if (!wmi_get_runtime_pm_inprogress(wma->wmi_handle)) { if (wake_info->vdev_id >= wma->max_bssid) { WMA_LOGE("%s: received invalid vdev_id %d", __func__, wake_info->vdev_id); return -EINVAL; } - WMA_LOGA("WOW wakeup host event received (reason: %s(%d)) for vdev %d", + + WMA_LOGA("WOW (%d) %s vdev:%d", + wake_info->wake_reason, wma_wow_wake_reason_str(wake_info->wake_reason, wma), wake_info->wake_reason, wake_info->vdev_id); - vos_wow_wakeup_host_event(wake_info->wake_reason); } vos_event_set(&wma->wma_resume_event); @@ -21555,6 +21554,12 @@ static int wma_wow_wakeup_host_event(void *handle, u_int8_t *event, #ifdef FEATURE_WLAN_SCAN_PNO case WOW_REASON_NLOD: wma_wow_wake_up_stats(wma, NULL, 0, WOW_REASON_NLOD); + if (wake_info->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, wake_info->vdev_id); + return -EINVAL; + } + node = &wma->interfaces[wake_info->vdev_id]; if (node) { WMA_LOGD("NLO match happened"); From 8ffb8d256d8617ecf2c46b896073d3cdf1285095 Mon Sep 17 00:00:00 2001 From: hqu Date: Mon, 22 Jan 2018 15:07:06 +0800 Subject: [PATCH 188/296] qcacld-2.0: Add mutex lock for proc handlers It will have race condition issue when multiple threads access some fields of global shared variable ctl concurrently. Fix is to add mutex lock for proc handlers. Bug: 35470735 Change-Id: Ifba428ae6544ccbdae0547a63972ab241ae68d7c CRs-Fixed: 2173232 Signed-off-by: Ahmed ElArabawy Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/UTILS/PKTLOG/linux_ac.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/linux_ac.c b/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/linux_ac.c index a3c470b678a0d..ed6db1766188b 100644 --- a/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/linux_ac.c +++ b/drivers/staging/qcacld-2.0/CORE/UTILS/PKTLOG/linux_ac.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -78,6 +78,8 @@ static struct ath_pktlog_info *g_pktlog_info; static struct proc_dir_entry *g_pktlog_pde; +static DEFINE_MUTEX(proc_mutex); + static int pktlog_attach(struct ol_softc *sc); static void pktlog_detach(struct ol_softc *sc); static int pktlog_open(struct inode *i, struct file *f); @@ -226,9 +228,11 @@ ATH_SYSCTL_DECL(ath_sysctl_pktlog_enable, ctl, write, filp, buffer, lenp, ol_ath_generic_softc_handle scn; struct ol_pktlog_dev_t *pl_dev; + mutex_lock(&proc_mutex); scn = (ol_ath_generic_softc_handle) ctl->extra1; if (!scn) { + mutex_unlock(&proc_mutex); printk("%s: Invalid scn context\n", __func__); ASSERT(0); return -EINVAL; @@ -237,6 +241,7 @@ ATH_SYSCTL_DECL(ath_sysctl_pktlog_enable, ctl, write, filp, buffer, lenp, pl_dev = get_pl_handle((struct ol_softc *)scn); if (!pl_dev) { + mutex_unlock(&proc_mutex); printk("%s: Invalid pktlog context\n", __func__); ASSERT(0); return -ENODEV; @@ -266,6 +271,7 @@ ATH_SYSCTL_DECL(ath_sysctl_pktlog_enable, ctl, write, filp, buffer, lenp, ctl->data = NULL; ctl->maxlen = 0; + mutex_unlock(&proc_mutex); return ret; } @@ -283,9 +289,11 @@ ATH_SYSCTL_DECL(ath_sysctl_pktlog_size, ctl, write, filp, buffer, lenp, ol_ath_generic_softc_handle scn; struct ol_pktlog_dev_t *pl_dev; + mutex_lock(&proc_mutex); scn = (ol_ath_generic_softc_handle) ctl->extra1; if (!scn) { + mutex_unlock(&proc_mutex); printk("%s: Invalid scn context\n", __func__); ASSERT(0); return -EINVAL; @@ -294,6 +302,7 @@ ATH_SYSCTL_DECL(ath_sysctl_pktlog_size, ctl, write, filp, buffer, lenp, pl_dev = get_pl_handle((struct ol_softc *)scn); if (!pl_dev) { + mutex_unlock(&proc_mutex); printk("%s: Invalid pktlog handle\n", __func__); ASSERT(0); return -ENODEV; @@ -318,6 +327,7 @@ ATH_SYSCTL_DECL(ath_sysctl_pktlog_size, ctl, write, filp, buffer, lenp, ctl->data = NULL; ctl->maxlen = 0; + mutex_unlock(&proc_mutex); return ret; } From e35fc78d6dde3cd2ae9bd24c0bfb3ebed9a2abc6 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Thu, 21 Dec 2017 16:27:03 +0530 Subject: [PATCH 189/296] qcacld-2.0: Fix buffer overwrite due to ssid_len in WMA handlers In multiple WMA event handler functions, ssid_len is used to copy ssid from FW buffer to local buffer and ssid_len value is received from the FW. If the ssid_len value exceeds SIR_MAC_MAX_SSID_LENGTH then a buffer overwrite would occur. Add sanity check for ssid_len against SIR_MAC_MAX_SSID_LENGTH in multiple WMA handler functions Bug: 72956801 Change-Id: I9e4b1f88c275093b4912496cdb936cf54a8880a2 CRs-Fixed: 2162678 Signed-off-by: Ecco Park Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 1b755e01de69e..d4be4bc23f3ce 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -4006,6 +4006,11 @@ static int wma_extscan_hotlist_match_event_handler(void *handle, dest_ap->ieLength = src_hotlist-> ie_length; WMI_MAC_ADDR_TO_CHAR_ARRAY(&src_hotlist->bssid, dest_ap->bssid); + if (src_hotlist->ssid.ssid_len > SIR_MAC_MAX_SSID_LENGTH) { + WMA_LOGE("%s Invalid SSID len %d, truncating", + __func__, src_hotlist->ssid.ssid_len); + src_hotlist->ssid.ssid_len = SIR_MAC_MAX_SSID_LENGTH; + } vos_mem_copy(dest_ap->ssid, src_hotlist->ssid.ssid, src_hotlist->ssid.ssid_len); dest_ap->ssid[src_hotlist->ssid.ssid_len] = '\0'; @@ -4180,6 +4185,13 @@ static int wma_group_num_bss_to_scan_id(const u_int8_t *cmd_param_info, WMI_MAC_ADDR_TO_CHAR_ARRAY(&src_hotlist->bssid, ap->bssid); + if (src_hotlist->ssid.ssid_len > + SIR_MAC_MAX_SSID_LENGTH) { + WMA_LOGD("%s Invalid SSID len %d, truncating", + __func__, src_hotlist->ssid.ssid_len); + src_hotlist->ssid.ssid_len = + SIR_MAC_MAX_SSID_LENGTH; + } vos_mem_copy(ap->ssid, src_hotlist->ssid.ssid, src_hotlist->ssid.ssid_len); ap->ssid[src_hotlist->ssid.ssid_len] = '\0'; @@ -4488,9 +4500,13 @@ static int wma_passpoint_match_event_handler(void *handle, WMA_SVC_MSG_MAX_SIZE) { WMA_LOGE("IE Length: %d or ANQP Length: %d is huge", event->ie_length, event->anqp_length); - VOS_ASSERT(0); return -EINVAL; } + if (event->ssid.ssid_len > SIR_MAC_MAX_SSID_LENGTH) { + WMA_LOGD("%s: Invalid ssid len %d, truncating", + __func__, event->ssid.ssid_len); + event->ssid.ssid_len = SIR_MAC_MAX_SSID_LENGTH; + } dest_match = vos_mem_malloc(sizeof(*dest_match) + event->ie_length + event->anqp_length); if (!dest_match) { From 174ee8671dd4d9afbbe523bc77e0d156f29ea4a7 Mon Sep 17 00:00:00 2001 From: "Padma, Santhosh Kumar" Date: Fri, 8 Dec 2017 17:48:10 +0530 Subject: [PATCH 190/296] qcacld-2.0: Avoid buffer overflow qcacld-3.0 to qcacld-2.0 propagation Add max check for probe request length against max length of probe request buffer to avoid buffer overflow. Bug: 72957234 Change-Id: Ie0fad7443b2c749c66bb9ad662625a16d3a840c3 CRs-Fixed: 2155808 Signed-off-by: Ahmed ElArabawy Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/MAC/src/pe/lim/limProcessProbeReqFrame.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessProbeReqFrame.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessProbeReqFrame.c index 4cef79044b99d..b6bb3ee09404f 100644 --- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessProbeReqFrame.c +++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessProbeReqFrame.c @@ -742,6 +742,10 @@ limSendSmeProbeReqInd(tpAniSirGlobal pMac, MTRACE(macTrace(pMac, TRACE_CODE_TX_SME_MSG, psessionEntry->peSessionId, msgQ.type)); + + if (ProbeReqIELen > sizeof(pSirSmeProbeReqInd->WPSPBCProbeReq.probeReqIE)) + ProbeReqIELen = sizeof(pSirSmeProbeReqInd->WPSPBCProbeReq.probeReqIE); + pSirSmeProbeReqInd->WPSPBCProbeReq.probeReqIELen = (tANI_U16)ProbeReqIELen; vos_mem_copy(pSirSmeProbeReqInd->WPSPBCProbeReq.probeReqIE, pProbeReqIE, ProbeReqIELen); From 3a81df52a23b9f10a5541048331a44fda26ec98a Mon Sep 17 00:00:00 2001 From: Tiger Yu Date: Wed, 6 Dec 2017 15:24:55 +0800 Subject: [PATCH 191/296] qcacld-2.0: Fix potential buffer overflow for TX_COMPL_IND Check for the validity of num_msdus when received the htt message of HTT_T2H_MSG_TYPE_TX_COMPL_IND or HTT_T2H_MSG_TYPE_TX_INSPECT_IND from firmware to ensure the buffer overflow does not happen. Bug: 72957505 Change-Id: Ic6ce75f34c5e2705d174eda014350e6ef0391388 CRs-Fixed: 2146869 Signed-off-by: Ahmed ElArabawy Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h | 3 ++ .../qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h index c608cf59413af..129443fe84b29 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h @@ -6315,6 +6315,9 @@ PREPACK struct htt_txq_group { #define HTT_TX_COMPL_IND_APPEND_GET(_info) \ (((_info) & HTT_TX_COMPL_IND_APPEND_M) >> HTT_TX_COMPL_IND_APPEND_S) +#define HTT_TX_COMPL_HEAD_SZ 4 +#define HTT_TX_COMPL_BYTES_PER_MSDU_ID 2 + #define HTT_TX_COMPL_CTXT_SZ sizeof(A_UINT16) #define HTT_TX_COMPL_CTXT_NUM(_bytes) ((_bytes) >> 1) diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c index f20152b1783d5..9b2a2f12a6b1d 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_t2h.c @@ -646,10 +646,26 @@ if (adf_os_unlikely(pdev->rx_ring.rx_reset)) { { int num_msdus; enum htt_tx_status status; + int msg_len = adf_nbuf_len(htt_t2h_msg); /* status - no enum translation needed */ status = HTT_TX_COMPL_IND_STATUS_GET(*msg_word); num_msdus = HTT_TX_COMPL_IND_NUM_GET(*msg_word); + + /* + * each desc id will occupy 2 bytes. + * the 4 is for htt msg header + */ + if ((num_msdus * HTT_TX_COMPL_BYTES_PER_MSDU_ID + + HTT_TX_COMPL_HEAD_SZ) > msg_len) { + adf_os_print("%s: num_msdus(%d) is invalid," + "adf_nbuf_len = %d\n", + __FUNCTION__, + num_msdus, + msg_len); + break; + } + if (num_msdus & 0x1) { struct htt_tx_compl_ind_base *compl = (void *)msg_word; @@ -718,8 +734,23 @@ if (adf_os_unlikely(pdev->rx_ring.rx_reset)) { case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: { int num_msdus; + int msg_len = adf_nbuf_len(htt_t2h_msg); num_msdus = HTT_TX_COMPL_IND_NUM_GET(*msg_word); + /* + * each desc id will occupy 2 bytes. + * the 4 is for htt msg header + */ + if ((num_msdus * HTT_TX_COMPL_BYTES_PER_MSDU_ID + + HTT_TX_COMPL_HEAD_SZ) > msg_len) { + adf_os_print("%s: num_msdus(%d) is invalid," + "adf_nbuf_len = %d,inspect\n", + __FUNCTION__, + num_msdus, + msg_len); + break; + } + if (num_msdus & 0x1) { struct htt_tx_compl_ind_base *compl = (void *)msg_word; From 1306eda7edef4b52a21fc01217a415c758c81745 Mon Sep 17 00:00:00 2001 From: Ashish Kumar Dhanotiya Date: Wed, 29 Nov 2017 14:04:38 +0530 Subject: [PATCH 192/296] qcacld-2.0: Avoid possible stack overflow in hdd_ProcessGENIE API There is no check for the return value of dot11fUnpackIeRSN API in hdd_ProcessGENIE API, which may cause stack overflow if pmkid_count is returned as more than the PMKIDCache size. Add a check for return value of dot11fUnpackIeRSN to avoid possible stack overflow. Bug: 72957507 Change-Id: I56424c706de121b18b8d3f2c4a35089ec0434452 CRs-Fixed: 2149187 Signed-off-by: Ecco Park Signed-off-by: anupritaisno1 --- .../staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c index 28fb6b23e96e2..d2543592861e7 100644 --- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c +++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c @@ -4418,6 +4418,7 @@ static tANI_S32 hdd_ProcessGENIE(hdd_adapter_t *pAdapter, tDot11fIERSN dot11RSNIE; tDot11fIEWPA dot11WPAIE; tANI_U32 i; + tANI_U32 status; tANI_U8 *pRsnIe; tANI_U16 RSNIeLen; tPmkidCacheInfo PMKIDCache[4]; // Local transfer memory @@ -4443,10 +4444,17 @@ static tANI_S32 hdd_ProcessGENIE(hdd_adapter_t *pAdapter, pRsnIe = gen_ie + 2; RSNIeLen = gen_ie_len - 2; // Unpack the RSN IE - dot11fUnpackIeRSN((tpAniSirGlobal) halHandle, + status = dot11fUnpackIeRSN((tpAniSirGlobal) halHandle, pRsnIe, RSNIeLen, &dot11RSNIE); + if (DOT11F_FAILED(status)) + { + hddLog(LOGE, + FL("Parse failure in hdd_ProcessGENIE (0x%08x)"), + status); + return -EINVAL; + } // Copy out the encryption and authentication types hddLog(LOG1, FL("%s: pairwise cipher suite count: %d"), __func__, dot11RSNIE.pwise_cipher_suite_count ); From 5790063ae4a06849cc90950a192f8a975a50d3b7 Mon Sep 17 00:00:00 2001 From: Tiger Yu Date: Tue, 2 Jan 2018 14:09:23 +0800 Subject: [PATCH 193/296] qcacld-2.0: Fix memory leak for txrx_fw_stats cmd The txrx_fw_stats cmd will allocate a req object before sending the cmd to the firmware, this memory is only freed when get response from firmware. The memory leak will appear if the firmware doesn't response in time before the driver unloading. This fix will add a pending queue to trace this req object in the pdev. when pdev is detaching, it will clean up this queue to avoid memory leak. Bug: 72957257 Change-Id: I35f6216d35befbab978bba161252b305488bd34c CRs-Fixed: 2113219 Signed-off-by: Ecco Park Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c | 65 +++++++++++++++---- .../CORE/CLD_TXRX/TXRX/ol_txrx_types.h | 4 ++ .../CORE/SERVICES/COMMON/ol_txrx_dbg.h | 7 ++ 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c index 7cd0917fc95d8..606209a9ac0be 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c @@ -376,6 +376,9 @@ ol_txrx_pdev_attach( TXRX_STATS_INIT(pdev); TAILQ_INIT(&pdev->vdev_list); + TAILQ_INIT(&pdev->req_list); + pdev->req_list_depth = 0; + adf_os_spinlock_init(&pdev->req_list_spinlock); /* do initial set up of the peer ID -> peer object lookup map */ if (ol_txrx_peer_find_attach(pdev)) { @@ -887,6 +890,8 @@ void ol_txrx_pdev_detach(ol_txrx_pdev_handle pdev, int force) { int i; + unsigned int page_idx; + struct ol_txrx_stats_req_internal *req; /*checking to ensure txrx pdev structure is not NULL */ if (!pdev) { @@ -899,6 +904,16 @@ ol_txrx_pdev_detach(ol_txrx_pdev_handle pdev, int force) /* check that the pdev has no vdevs allocated */ TXRX_ASSERT1(TAILQ_EMPTY(&pdev->vdev_list)); + adf_os_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_FOREACH(req, &pdev->req_list, req_list_elem) { + TAILQ_REMOVE(&pdev->req_list, req, req_list_elem); + pdev->req_list_depth--; + adf_os_mem_free(req); + } + adf_os_spin_unlock_bh(&pdev->req_list_spinlock); + + adf_os_spinlock_destroy(&pdev->req_list_spinlock); + OL_RX_REORDER_TIMEOUT_CLEANUP(pdev); if (ol_cfg_is_high_latency(pdev->ctrl_pdev)) { @@ -1961,12 +1976,6 @@ void ol_txrx_print_level_set(unsigned level) #endif } -struct ol_txrx_stats_req_internal { - struct ol_txrx_stats_req base; - int serviced; /* state of this request */ - int offset; -}; - static inline u_int64_t OL_TXRX_STATS_PTR_TO_U64(struct ol_txrx_stats_req_internal *req) { @@ -2028,6 +2037,11 @@ ol_txrx_fw_stats_get( /* use the non-volatile request object's address as the cookie */ cookie = OL_TXRX_STATS_PTR_TO_U64(non_volatile_req); + adf_os_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_INSERT_TAIL(&pdev->req_list, non_volatile_req, req_list_elem); + pdev->req_list_depth++; + adf_os_spin_unlock_bh(&pdev->req_list_spinlock); + if (htt_h2t_dbg_stats_get( pdev->htt_pdev, req->stats_type_upload_mask, @@ -2035,14 +2049,15 @@ ol_txrx_fw_stats_get( HTT_H2T_STATS_REQ_CFG_STAT_TYPE_INVALID, 0, cookie)) { + adf_os_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_REMOVE(&pdev->req_list, non_volatile_req, req_list_elem); + pdev->req_list_depth--; + adf_os_spin_unlock_bh(&pdev->req_list_spinlock); + adf_os_mem_free(non_volatile_req); return A_ERROR; } - if (req->wait.blocking) { - while (adf_os_mutex_acquire(pdev->osdev, req->wait.sem_ptr)) {} - } - return A_OK; } #endif @@ -2056,11 +2071,27 @@ ol_txrx_fw_stats_handler( enum htt_dbg_stats_status status; int length; u_int8_t *stats_data; - struct ol_txrx_stats_req_internal *req; + struct ol_txrx_stats_req_internal *req, *tmp; int more = 0; + int found = 0; req = OL_TXRX_U64_TO_STATS_PTR(cookie); + adf_os_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_FOREACH(tmp, &pdev->req_list, req_list_elem) { + if (req == tmp) { + found = 1; + break; + } + } + adf_os_spin_unlock_bh(&pdev->req_list_spinlock); + + if (!found) { + TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, + "req(%p) from firmware can't be found in the list\n", req); + return; + } + do { htt_t2h_dbg_stats_hdr_parse( stats_info_list, &type, &status, &length, &stats_data); @@ -2184,10 +2215,16 @@ ol_txrx_fw_stats_handler( } while (1); if (! more) { - if (req->base.wait.blocking) { - adf_os_mutex_release(pdev->osdev, req->base.wait.sem_ptr); + adf_os_spin_lock_bh(&pdev->req_list_spinlock); + TAILQ_FOREACH(tmp, &pdev->req_list, req_list_elem) { + if (req == tmp) { + TAILQ_REMOVE(&pdev->req_list, req, req_list_elem); + pdev->req_list_depth--; + adf_os_mem_free(req); + break; + } } - adf_os_mem_free(req); + adf_os_spin_unlock_bh(&pdev->req_list_spinlock); } } diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h index ff889caff7c04..2873e9d3c3215 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h @@ -544,6 +544,10 @@ struct ol_txrx_pdev_t { /* ol_txrx_vdev list */ TAILQ_HEAD(, ol_txrx_vdev_t) vdev_list; + TAILQ_HEAD(, ol_txrx_stats_req_internal) req_list; + int req_list_depth; + adf_os_spinlock_t req_list_spinlock; + /* peer ID to peer object map (array of pointers to peer objects) */ struct ol_txrx_peer_t **peer_id_to_obj_map; diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_dbg.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_dbg.h index 7309db33dc865..517bf5f4b3175 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_dbg.h +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_dbg.h @@ -76,6 +76,13 @@ struct ol_txrx_stats_req { } wait; }; +struct ol_txrx_stats_req_internal { + struct ol_txrx_stats_req base; + TAILQ_ENTRY(ol_txrx_stats_req_internal) req_list_elem; + int serviced; /* state of this request */ + int offset; +}; + #ifndef TXRX_DEBUG_LEVEL #define TXRX_DEBUG_LEVEL 0 /* no debug info */ #endif From 7e5e4acb47e69d2f6452fef98d5d9d6fc13e7a93 Mon Sep 17 00:00:00 2001 From: tfyu Date: Tue, 26 Sep 2017 14:42:31 +0800 Subject: [PATCH 194/296] qcacld-2.0: Dump the txrx stat req if the queue is not empty Dump the txrx stat req if the queue is not empty when detatch the pdev. Bug: 72957257 Change-Id: Ic38e01668efd28baf55acb04f448e236cc224c79 CRs-Fixed: 2113219 Signed-off-by: Ecco Park Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c index 606209a9ac0be..98961ca06d3a3 100644 --- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c +++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c @@ -889,7 +889,7 @@ A_STATUS ol_txrx_pdev_attach_target(ol_txrx_pdev_handle pdev) void ol_txrx_pdev_detach(ol_txrx_pdev_handle pdev, int force) { - int i; + int i = 0; unsigned int page_idx; struct ol_txrx_stats_req_internal *req; @@ -905,9 +905,23 @@ ol_txrx_pdev_detach(ol_txrx_pdev_handle pdev, int force) TXRX_ASSERT1(TAILQ_EMPTY(&pdev->vdev_list)); adf_os_spin_lock_bh(&pdev->req_list_spinlock); + if (pdev->req_list_depth > 0) + TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, + "Warning: the txrx req list is not empty, depth=%d\n", + pdev->req_list_depth + ); TAILQ_FOREACH(req, &pdev->req_list, req_list_elem) { TAILQ_REMOVE(&pdev->req_list, req, req_list_elem); pdev->req_list_depth--; + TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, + "%d: %p,verbose(%d), concise(%d), up_m(0x%x), reset_m(0x%x)\n", + i++, + req, + req->base.print.verbose, + req->base.print.concise, + req->base.stats_type_upload_mask, + req->base.stats_type_reset_mask + ); adf_os_mem_free(req); } adf_os_spin_unlock_bh(&pdev->req_list_spinlock); From 9eb19b077ccbcf00a4d5132dca3cefc393995002 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 6 Dec 2017 12:30:30 +0530 Subject: [PATCH 195/296] qcacld-2.0: Fix potential buffer overwrite In function wma_unified_link_iface_stats_event_handler, num_ac is received from the firmware and is used in the loop to populate values into results. However, the memory for results is allocated only for WIFI_AC_MAX and a buffer overflow will occur if num_ac is greater than or equal to WIFI_AC_MAX. Add checks to make sure num_ac is not greater than to WIFI_AC_MAX. Note: This CL has been updated to fix the check to only return error on num_ac > WIFI_AC_MAX since an equal value is a valid value. This is to fix b/73597588. Bug: 70237689 Change-Id: Ie2056017aae641236efb118889e2919795b60f18 CRs-Fixed: 2154226 Signed-off-by: Ahmed ElArabawy Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index d4be4bc23f3ce..8ebf327671c3d 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -4598,6 +4598,11 @@ static int wma_unified_link_iface_stats_event_handler(void *handle, WMA_LOGA("%s: Invalid param_tlvs for Iface Stats", __func__); return -EINVAL; } + if (link_stats->num_ac > WIFI_AC_MAX) { + WMA_LOGE("%s: Excess data received from firmware num_ac %d", + __func__, link_stats->num_ac); + return -EINVAL; + } link_stats_size = sizeof(tSirWifiIfaceStat); iface_info_size = sizeof(tSirWifiInterfaceInfo); From f9dd3a415e61cdcc1e7b88a7fd276e98236df54b Mon Sep 17 00:00:00 2001 From: Vignesh Viswanathan Date: Tue, 12 Dec 2017 15:33:34 +0530 Subject: [PATCH 196/296] qcacld-2.0: Fix potential buffer overwrite in wma_vdev_start_rsp_ind In function wma_vdev_start_rsp_ind, vdev_id is received from the FW and is used to access wma_handle->interfaces without validating the upper limit. If the value of vdev_id received from the FW is not less than max_bssid, then a buffer overwrite will occur in the function wma_vdev_start_rsp_ind. Add sanity check to make sure vdev_id is less than max_bssid. Bug: 72957725 Change-Id: I83e1b797ca50a7fb58519f66dde26b035a2393ce CRs-Fixed: 2150359 Signed-off-by: Ahmed ElArabawy Signed-off-by: anupritaisno1 --- .../qcacld-2.0/CORE/SERVICES/WMA/wma.c | 30 +++++++++++-------- .../CORE/SERVICES/WMA/wma_nan_datapath.c | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index 8ebf327671c3d..f6965b4b0ada8 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -498,7 +498,7 @@ static bool wma_is_vdev_in_ap_mode(tp_wma_handle wma, u_int8_t vdev_id) { struct wma_txrx_node *intf = wma->interfaces; - if (vdev_id > wma->max_bssid) { + if (vdev_id >= wma->max_bssid) { WMA_LOGP("%s: Invalid vdev_id %hu", __func__, vdev_id); VOS_ASSERT(0); return false; @@ -524,7 +524,7 @@ static bool wma_is_vdev_in_ibss_mode(tp_wma_handle wma, u_int8_t vdev_id) { struct wma_txrx_node *intf = wma->interfaces; - if (vdev_id > wma->max_bssid) { + if (vdev_id >= wma->max_bssid) { WMA_LOGP("%s: Invalid vdev_id %hu", __func__, vdev_id); VOS_ASSERT(0); return false; @@ -1300,9 +1300,15 @@ static int wma_vdev_start_rsp_ind(tp_wma_handle wma, u_int8_t *buf) return -EINVAL; } + if (resp_event->vdev_id >= wma->max_bssid) { + WMA_LOGE("%s: received invalid vdev_id %d", + __func__, resp_event->vdev_id); + return -EINVAL; + } + iface = &wma->interfaces[resp_event->vdev_id]; - if ((resp_event->vdev_id <= wma->max_bssid) && + if ((resp_event->vdev_id < wma->max_bssid) && (adf_os_atomic_read( &wma->interfaces[resp_event->vdev_id].vdev_restart_params.hidden_ssid_restart_in_progress)) && (wma_is_vdev_in_ap_mode(wma, resp_event->vdev_id) == true)) { @@ -1903,7 +1909,7 @@ static void wma_delete_all_ibss_peers(tp_wma_handle wma, A_UINT32 vdev_id) ol_txrx_vdev_handle vdev; ol_txrx_peer_handle peer, temp; - if (!wma || vdev_id > wma->max_bssid) + if (!wma || vdev_id >= wma->max_bssid) return; vdev = wma->interfaces[vdev_id].handle; @@ -1945,7 +1951,7 @@ static void wma_delete_all_ap_remote_peers(tp_wma_handle wma, A_UINT32 vdev_id) ol_txrx_vdev_handle vdev; ol_txrx_peer_handle peer, temp; - if (!wma || vdev_id > wma->max_bssid) + if (!wma || vdev_id >= wma->max_bssid) return; vdev = wma->interfaces[vdev_id].handle; @@ -2143,7 +2149,7 @@ static int wma_vdev_stop_ind(tp_wma_handle wma, u_int8_t *buf) resp_event = (wmi_vdev_stopped_event_fixed_param *)buf; - if ((resp_event->vdev_id <= wma->max_bssid) && + if ((resp_event->vdev_id < wma->max_bssid) && (adf_os_atomic_read(&wma->interfaces[resp_event->vdev_id].vdev_restart_params.hidden_ssid_restart_in_progress)) && ((wma->interfaces[resp_event->vdev_id].type == WMI_VDEV_TYPE_AP) && (wma->interfaces[resp_event->vdev_id].sub_type == 0))) { @@ -2182,7 +2188,7 @@ static int wma_vdev_stop_ind(tp_wma_handle wma, u_int8_t *buf) tpDeleteBssParams params = (tpDeleteBssParams)req_msg->user_data; struct beacon_info *bcn; - if (resp_event->vdev_id > wma->max_bssid) { + if (resp_event->vdev_id >= wma->max_bssid) { WMA_LOGE("%s: Invalid vdev_id %d", __func__, resp_event->vdev_id); vos_mem_free(params); @@ -9804,7 +9810,7 @@ VOS_STATUS wma_start_scan(tp_wma_handle wma_handle, int len; tSirScanOffloadEvent *scan_event; - if (scan_req->sessionId > wma_handle->max_bssid) { + if (scan_req->sessionId >= wma_handle->max_bssid) { WMA_LOGE("%s: Invalid vdev_id %d, msg_type : 0x%x", __func__, scan_req->sessionId, msg_type); goto error1; @@ -12775,7 +12781,7 @@ void wma_vdev_resp_timer(void *data) struct beacon_info *bcn; struct wma_txrx_node *iface; - if (tgt_req->vdev_id > wma->max_bssid) { + if (tgt_req->vdev_id >= wma->max_bssid) { WMA_LOGE("%s: Invalid vdev_id %d", __func__, tgt_req->vdev_id); vos_mem_free(params); @@ -23244,7 +23250,7 @@ static VOS_STATUS wma_wow_enter(tp_wma_handle wma, WMA_LOGD("wow enable req received for vdev id: %d", info->sessionId); - if (info->sessionId > wma->max_bssid) { + if (info->sessionId >= wma->max_bssid) { WMA_LOGE("Invalid vdev id (%d)", info->sessionId); vos_mem_free(info); return VOS_STATUS_E_INVAL; @@ -23271,7 +23277,7 @@ static VOS_STATUS wma_wow_exit(tp_wma_handle wma, WMA_LOGD("wow disable req received for vdev id: %d", info->sessionId); - if (info->sessionId > wma->max_bssid) { + if (info->sessionId >= wma->max_bssid) { WMA_LOGE("Invalid vdev id (%d)", info->sessionId); vos_mem_free(info); return VOS_STATUS_E_INVAL; @@ -23304,7 +23310,7 @@ static VOS_STATUS wma_suspend_req(tp_wma_handle wma, tpSirWlanSuspendParam info) wma->no_of_suspend_ind++; - if (info->sessionId > wma->max_bssid) { + if (info->sessionId >= wma->max_bssid) { WMA_LOGE("Invalid vdev id (%d)", info->sessionId); vos_mem_free(info); return VOS_STATUS_E_INVAL; diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c index 2d2909e6b3912..f5ea7ac92498f 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c @@ -1138,7 +1138,7 @@ void wma_delete_all_nan_remote_peers(tp_wma_handle wma, uint32_t vdev_id) ol_txrx_vdev_handle vdev; ol_txrx_peer_handle peer, temp; - if (!wma || vdev_id > wma->max_bssid) + if (!wma || vdev_id >= wma->max_bssid) return; vdev = wma->interfaces[vdev_id].handle; From 7eb57160492b25f7a229dd9b488f2f117386f204 Mon Sep 17 00:00:00 2001 From: Zhang Qian Date: Mon, 4 Dec 2017 19:07:32 +0800 Subject: [PATCH 197/296] qcacld-2.0: Fix potential buffer overflow Fragment count will be larger than the upper limit of cvg_nbuf_cb->extra_flag.num which would lead to an overread of fragment length. Upper limit check for fragment count is added in this change. Change-Id: Icc078b2efee554ac84377b5edd90d0a5c7a61f98 CRs-Fixed: 2129566 Bug: 72957387 Signed-off-by: Ecco Park Signed-off-by: anupritaisno1 --- .../CORE/SERVICES/HIF/USB/hif_usb.c | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/USB/hif_usb.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/USB/hif_usb.c index a6a88bf2754c8..caae8f0e3c2a5 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/USB/hif_usb.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/USB/hif_usb.c @@ -245,7 +245,8 @@ static A_STATUS HIFSend_internal(HIF_DEVICE *hifDevice, a_uint8_t PipeID, int usb_status; int i; struct HIFSendContext *pSendContext; - int frag_count = 0, head_data_len, tmp_frag_count = 0; + uint8_t frag_count; + uint32_t head_data_len, tmp_frag_count = 0; unsigned char *pData; AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_OUT, ("+%s pipe : %d, buf:0x%p\n", @@ -254,8 +255,15 @@ static A_STATUS HIFSend_internal(HIF_DEVICE *hifDevice, a_uint8_t PipeID, a_mem_trace(buf); frag_count = adf_nbuf_get_num_frags(buf); - if (frag_count > 1) { /* means have extra fragment buf in skb */ - /* header data length should be total sending length substract + if (frag_count == 1) { + /* + * | HIFSendContext | netbuf->data + */ + head_data_len = sizeof(struct HIFSendContext); + } else if ((frag_count - 1) <= CVG_NBUF_MAX_EXTRA_FRAGS) { + /* + * means have extra fragment buf in skb + * header data length should be total sending length substract * internal data length of netbuf * | HIFSendContext | fragments except internal buffer | * netbuf->data @@ -268,10 +276,12 @@ static A_STATUS HIFSend_internal(HIF_DEVICE *hifDevice, a_uint8_t PipeID, tmp_frag_count = tmp_frag_count + 1; } } else { - /* - * | HIFSendContext | netbuf->data - */ - head_data_len = sizeof(struct HIFSendContext); + /* Extra fragments overflow */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ( + "%s Extra fragments count overflow : %d\n", + __func__, frag_count)); + status = A_ERROR; + goto exit; } /* Check whether head room is enough to save extra head data */ @@ -366,6 +376,7 @@ static A_STATUS HIFSend_internal(HIF_DEVICE *hifDevice, a_uint8_t PipeID, } while (FALSE); +exit: if (A_FAILED(status) && (status != A_NO_RESOURCE)) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("athusb send failed %d\n", status)); From d30046405893e18677261307e73d82ad6e874c0c Mon Sep 17 00:00:00 2001 From: tinlin Date: Thu, 11 Jan 2018 15:45:27 +0800 Subject: [PATCH 198/296] qcacld-2.0: Add data_len check to avoid OOB access Add data_len check in wma_nan_rsp_event_handler() to avoid OOB access. Bug: 74237168 Change-Id: Iff42da84567381a4b64bc07e69ff1a0cd4b5a543 CRs-Fixed: 2170630 Signed-off-by: anupritaisno1 --- drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c index f6965b4b0ada8..11aeea337a431 100644 --- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c +++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c @@ -6335,7 +6335,8 @@ static int wma_nan_rsp_event_handler(void *handle, u_int8_t *event_buf, alloc_len = sizeof(tSirNanEvent); alloc_len += nan_rsp_event_hdr->data_len; if (nan_rsp_event_hdr->data_len > ((WMA_SVC_MSG_MAX_SIZE - - sizeof(*nan_rsp_event_hdr)) / sizeof(u_int8_t))) { + sizeof(*nan_rsp_event_hdr)) / sizeof(u_int8_t)) || + nan_rsp_event_hdr->data_len > param_buf->num_data) { WMA_LOGE("excess data length:%d", nan_rsp_event_hdr->data_len); VOS_ASSERT(0); return -EINVAL; From 4f0494d0e3572b4cc44e611c03cfb59650ebb8e0 Mon Sep 17 00:00:00 2001 From: Lianjun Huang Date: Sat, 16 Jun 2018 22:59:46 +0800 Subject: [PATCH 199/296] ANDROID: sdcardfs: fix potential crash when reserved_mb is not zero sdcardfs_mkdir() calls check_min_free_space(). When reserved_mb is not zero, a negative dentry will be passed to ext4_statfs() at last and ext4_statfs() will crash. The parent dentry is positive. So we use the parent dentry to check free space. Change-Id: I80ab9623fe59ba911f4cc9f0e029a1c6f7ee421b Signed-off-by: Lianjun Huang Signed-off-by: anupritaisno1 --- fs/sdcardfs/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index ccca427679deb..7386e1b49d759 100755 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -270,6 +270,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_parent_dentry = NULL; + struct dentry *parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; @@ -289,11 +290,14 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* check disk space */ - if (!check_min_free_space(dentry, 0, 1)) { + parent_dentry = dget_parent(dentry); + if (!check_min_free_space(parent_dentry, 0, 1)) { pr_err("sdcardfs: No minimum free space.\n"); err = -ENOSPC; + dput(parent_dentry); goto out_revert; } + dput(parent_dentry); /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); From 009730f4c7ba59843a0bc8a37876818cec30990d Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Thu, 28 Sep 2017 10:54:21 -0700 Subject: [PATCH 200/296] 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 Signed-off-by: anupritaisno1 --- include/sound/pcm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 42d830afcf672..4c0e95b47213f 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -397,6 +397,7 @@ struct snd_pcm_substream { 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 */ From 2fc793d4986508694b89de52f5517c6abd2a36ea Mon Sep 17 00:00:00 2001 From: Raghavendra Kakarla Date: Thu, 21 Dec 2017 16:24:31 +0530 Subject: [PATCH 201/296] 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 Signed-off-by: anupritaisno1 --- 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 1c0a05c87edaf..8194fe0cda5b6 100644 --- a/drivers/soc/qcom/rpm-smd-debug.c +++ b/drivers/soc/qcom/rpm-smd-debug.c @@ -99,23 +99,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, From 8d5ef616c0e0b5b5060463d7c6b894f6520144e4 Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Wed, 25 Oct 2017 16:16:36 +0530 Subject: [PATCH 202/296] 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. CRs-Fixed: 2129451 Change-Id: I0b3511cff7e2917fe83bddfc15086e939f5c2abc Signed-off-by: Jitendra Sharma Signed-off-by: Swetha Chikkaboraiah Signed-off-by: anupritaisno1 --- drivers/soc/qcom/subsys-pil-tz.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index 001cc25bd4e3a..cd44f5ae6db99 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -969,6 +969,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev) destroy_ramdump_device(d->ramdump_dev); err_ramdump: pil_desc_release(&d->desc); + platform_set_drvdata(pdev, NULL); return rc; } From a406fd06ee40daffaccd7e95db8bd7321b998f1a Mon Sep 17 00:00:00 2001 From: Neeraj Soni Date: Fri, 25 Nov 2016 14:24:35 +0530 Subject: [PATCH 203/296] qseecom: Add new ioctl to export qsee version Export new ioctl to user space to know the qsee version, which is required for QSEECOM listener services. Change-Id: Idd80ce0a3153d669d5f6fb748f73f7aaedefb3a5 Signed-off-by: Neeraj Soni Signed-off-by: anupritaisno1 --- drivers/misc/qseecom.c | 27 ++++++++++++++++++++++++++- include/uapi/linux/qseecom.h | 9 +++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 01732976d44f6..1d38f4d7e41aa 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -1,6 +1,6 @@ /*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver * - * 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 @@ -3448,6 +3448,23 @@ static int qseecom_get_qseos_version(struct qseecom_dev_handle *data, return 0; } +static int qseecom_get_qsee_version(struct qseecom_dev_handle *data, + void __user *argp) +{ + struct qseecom_qsee_version_req req; + + if (copy_from_user(&req, argp, sizeof(req))) { + pr_err("copy_from_user failed\n"); + return -EINVAL; + } + req.qsee_version = qseecom.qsee_version; + if (copy_to_user(argp, &req, sizeof(req))) { + pr_err("copy_to_user failed\n"); + return -EINVAL; + } + return 0; +} + static int __qseecom_enable_clk(enum qseecom_ce_hw_instance ce) { int rc = 0; @@ -5312,6 +5329,14 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) atomic_dec(&data->ioctl_count); break; } + case QSEECOM_IOCTL_GET_QSEE_VERSION_REQ: { + atomic_inc(&data->ioctl_count); + ret = qseecom_get_qsee_version(data, argp); + if (ret) + pr_err("qseecom_get_qseos_version: %d\n", ret); + atomic_dec(&data->ioctl_count); + break; + } case QSEECOM_IOCTL_PERF_ENABLE_REQ:{ if ((data->type != QSEECOM_GENERIC) && (data->type != QSEECOM_CLIENT_APP)) { diff --git a/include/uapi/linux/qseecom.h b/include/uapi/linux/qseecom.h index aac13eef45830..fae2e05552280 100644 --- a/include/uapi/linux/qseecom.h +++ b/include/uapi/linux/qseecom.h @@ -107,6 +107,13 @@ struct qseecom_qseos_version_req { unsigned int qseos_version; /* in */ }; +/* + * struct qseecom_qsee_version_req - get qsee version + * @qsee_version - version number + */ +struct qseecom_qsee_version_req { + unsigned int qsee_version; +}; /* * struct qseecom_qseos_app_load_query - verify if app is loaded in qsee * @app_name[MAX_APP_NAME_SIZE]- name of the app. @@ -293,4 +300,6 @@ extern long qseecom_ioctl(struct file *file, #define QSEECOM_QTEEC_IOCTL_REQUEST_CANCELLATION_REQ \ _IOWR(QSEECOM_IOC_MAGIC, 33, struct qseecom_qteec_modfd_req) +#define QSEECOM_IOCTL_GET_QSEE_VERSION_REQ \ + _IOWR(QSEECOM_IOC_MAGIC, 37, struct qseecom_qsee_version_req) #endif /* _UAPI_QSEECOM_H_ */ From 834ca453cca9dab83c607b3c532af1d0b1ea4ceb Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Tue, 16 Jan 2018 14:51:15 +0530 Subject: [PATCH 204/296] 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 Signed-off-by: anupritaisno1 --- 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 04851ab4796a4..f0df6eee63ca0 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -653,7 +653,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) From 6c247ca5c1aebe61d91192acffcdb15d8bd48b45 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 15 Jan 2018 16:10:30 -0800 Subject: [PATCH 205/296] 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 Signed-off-by: anupritaisno1 --- sound/soc/codecs/wcd_cpe_core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index 965caf8d4e8a1..a433bd2b18dcd 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -1,4 +1,4 @@ -/* 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 @@ -290,6 +290,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; From 998445c76c42de5e71e62468dc07a4d17c7a63b5 Mon Sep 17 00:00:00 2001 From: smanag Date: Tue, 14 Nov 2017 14:57:57 +0530 Subject: [PATCH 206/296] 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 Signed-off-by: anupritaisno1 --- drivers/soc/qcom/qdsp6v2/voice_svc.c | 40 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c index fe54589744061..e05230255d6f0 100644 --- a/drivers/soc/qcom/qdsp6v2/voice_svc.c +++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c @@ -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; +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 @@ static int voice_svc_dummy_reg() 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: From 70a29898693ed951ad4d3d123c6c5a8a284d3242 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Thu, 18 Jan 2018 00:41:48 -0800 Subject: [PATCH 207/296] 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 Signed-off-by: anupritaisno1 --- sound/soc/msm/qdsp6v2/q6asm.c | 139 +++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 34 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index fa27343fd2d20..c3d3814272b5b 100755 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -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; @@ -408,8 +413,8 @@ 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; } } @@ -417,34 +422,41 @@ 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; + int session_id; + unsigned long flags; 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; - 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); - } - list_del(&ac->no_wait_que); + kfree(ac); + spin_unlock_irqrestore(&(session[session_id].session_lock), flags); return; } @@ -990,8 +1002,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; @@ -1060,6 +1070,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; @@ -1137,7 +1148,6 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) fail_apr1: q6asm_session_free(ac); fail_session: - kfree(ac); return NULL; } @@ -1152,11 +1162,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; } @@ -1361,6 +1371,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; @@ -1409,9 +1420,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; } @@ -1461,6 +1479,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; } @@ -1496,6 +1518,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; } @@ -1522,7 +1548,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__); @@ -1532,15 +1559,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; } @@ -1555,7 +1588,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; @@ -1571,7 +1603,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->cmd_state, 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; } @@ -1582,9 +1615,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]) { @@ -1606,6 +1646,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: @@ -1633,6 +1675,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->cmd_state, -payload[1]); wake_up(&ac->cmd_wait); } + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return 0; } if (atomic_read(&ac->cmd_state) && wakeup_flag) { @@ -1653,6 +1698,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) { @@ -1689,6 +1737,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; } @@ -1702,6 +1753,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); @@ -1715,6 +1769,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; @@ -1786,6 +1843,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); @@ -1854,7 +1914,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; } @@ -1996,11 +2057,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; } @@ -2018,6 +2084,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; } @@ -6510,7 +6578,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(void) @@ -6728,7 +6796,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 */ From e273c78974d46b161abb45d8a5399eb56aa9a276 Mon Sep 17 00:00:00 2001 From: David Dai Date: Mon, 25 Sep 2017 15:16:23 -0700 Subject: [PATCH 208/296] 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 Signed-off-by: anupritaisno1 --- 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 94e94f3bbc1c7..1d6a581341a5f 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, From 47fabcacba91e242e929846a0f53020ab2cff4fc Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Thimmanna Bhattar Date: Tue, 13 Feb 2018 14:05:20 +0530 Subject: [PATCH 209/296] msm: thermal: Pass correct size of voltage table to IOCTL Fix issue in msm_thermal_process_voltage_table_request. For voltage tables bigger than 16 it is not correclty passing the partial table size to user space. The full size is passed instead of the partial size. For example if reading a table of 18 values the first read returns 16, and the partial read returns 18 but it should return 2. Change-Id: I75943e94341388cca772ee45bc1275fb5d2091d2 Signed-off-by: Jeff Bernard Signed-off-by: Santhosh Kumar Thimmanna Bhattar Signed-off-by: anupritaisno1 --- drivers/thermal/msm_thermal-dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/msm_thermal-dev.c b/drivers/thermal/msm_thermal-dev.c index eb0490a9cad81..ead9765666c84 100644 --- a/drivers/thermal/msm_thermal-dev.c +++ b/drivers/thermal/msm_thermal-dev.c @@ -268,6 +268,7 @@ static long msm_thermal_process_voltage_table_req( voltage->voltage_table[idx] = voltage_table_ptr[cluster_id][table_idx]; } + voltage->voltage_table_len = idx; copy_and_return: ret = copy_to_user((void __user *)(*arg), query, From 23ba138e31e0d613bfa392c467e50e45efd13199 Mon Sep 17 00:00:00 2001 From: Sriharsha P V Date: Wed, 31 Jan 2018 17:45:01 +0530 Subject: [PATCH 210/296] msm: kgsl: Increase memstore size Increase the memstore size to increase the number of the kgsl context that can be supported simultaneously. Signed-off-by: Sriharsha P V Change-Id: I295dfd057cf5869a77b265262b38b41fca3cf3ba Signed-off-by: anupritaisno1 --- drivers/gpu/msm/kgsl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 0711c43c31783..20de789fbd05e 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -30,7 +30,7 @@ /* The number of memstore arrays limits the number of contexts allowed. * If more contexts are needed, update multiple for MEMSTORE_SIZE */ -#define KGSL_MEMSTORE_SIZE ((int)(PAGE_SIZE * 2)) +#define KGSL_MEMSTORE_SIZE ((int)(PAGE_SIZE * 8)) #define KGSL_MEMSTORE_GLOBAL (0) #define KGSL_PRIORITY_MAX_RB_LEVELS 4 #define KGSL_MEMSTORE_MAX (KGSL_MEMSTORE_SIZE / \ From bcf1a0de18e2af6d914c8081e564c34c707d9b82 Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Wed, 31 Jan 2018 14:49:37 +0200 Subject: [PATCH 211/296] 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 Signed-off-by: anupritaisno1 --- drivers/platform/msm/ipa/ipa_rt.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/platform/msm/ipa/ipa_rt.c b/drivers/platform/msm/ipa/ipa_rt.c index 9d2f1df371030..db3c469cb83f2 100644 --- a/drivers/platform/msm/ipa/ipa_rt.c +++ b/drivers/platform/msm/ipa/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 @@ -1072,6 +1072,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("Default tbl last rule cannot be deleted\n"); + return -EINVAL; + } + } + if (entry->hdr) __ipa_release_hdr(entry->hdr->id); else if (entry->proc_ctx) From 644afc899705c267efd7b1a189414b175cf0330e Mon Sep 17 00:00:00 2001 From: Hardik Arya Date: Wed, 17 Jan 2018 21:03:52 +0530 Subject: [PATCH 212/296] 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 Signed-off-by: Bharat Pawar Signed-off-by: anupritaisno1 --- drivers/char/diag/diag_dci.c | 91 ++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 57b43001caec6..444e79849799a 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -1099,18 +1099,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. - */ - /* The length field indicates the total length removing the cmd_code + /* + * 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); @@ -1127,30 +1140,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 @@ -1266,19 +1309,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 */ list_for_each_safe(start, temp, &driver->dci_client_list) { entry = list_entry(start, struct diag_dci_client_tbl, track); From ecefb80412cd0d611ac2e562d28d19ca20f67100 Mon Sep 17 00:00:00 2001 From: Pratham Pratap Date: Thu, 15 Mar 2018 12:08:54 +0530 Subject: [PATCH 213/296] usb: dwc3: dbm: Fix double free in msm_dbm_probe Memory allocated with devm_kzalloc is automatically released by the kernel if the probe function fails with an error code. Therefore, using kfree is unsafe since it can lead to the Double-Free security issue. This change removes kfree from msm_dbm_probe function to avoid double free for dbm_data. Change-Id: I512284d021ba89d5d04a6d498aa17489e37bff2e Signed-off-by: Pratham Pratap Signed-off-by: anupritaisno1 --- drivers/usb/dwc3/dbm-1_4.c | 27 ++++----------------------- drivers/usb/dwc3/dbm-1_5.c | 27 ++++----------------------- 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/drivers/usb/dwc3/dbm-1_4.c b/drivers/usb/dwc3/dbm-1_4.c index 661a938e055c5..1caf7983f60f4 100644 --- a/drivers/usb/dwc3/dbm-1_4.c +++ b/drivers/usb/dwc3/dbm-1_4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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 @@ -372,7 +372,6 @@ static int msm_dbm_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dbm *dbm; struct resource *res; - int ret = 0; dbm_data = devm_kzalloc(dev, sizeof(*dbm_data), GFP_KERNEL); if (!dbm_data) @@ -382,24 +381,21 @@ static int msm_dbm_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "missing memory base resource\n"); - ret = -ENODEV; - goto free_dbm_data; + return -ENODEV; } dbm_data->base = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); if (!dbm_data->base) { dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto free_dbm_data; + return -ENOMEM; } dbm = devm_kzalloc(dev, sizeof(*dbm), GFP_KERNEL); if (!dbm) { dev_err(&pdev->dev, "not enough memory\n"); - ret = -ENOMEM; - goto free_dbm_data; + return -ENOMEM; } dbm->dev = dev; @@ -418,20 +414,6 @@ static int msm_dbm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dbm); return usb_add_dbm(dbm); - -free_dbm_data: - kfree(dbm_data); - return ret; -} - -static int msm_dbm_remove(struct platform_device *pdev) -{ - struct dbm *dbm = platform_get_drvdata(pdev); - - kfree(dbm); - kfree(dbm_data); - - return 0; } static const struct of_device_id msm_dbm_1_4_id_table[] = { @@ -444,7 +426,6 @@ MODULE_DEVICE_TABLE(of, msm_dbm_1_4_id_table); static struct platform_driver msm_dbm_driver = { .probe = msm_dbm_probe, - .remove = msm_dbm_remove, .driver = { .name = "msm-usb-dbm-1-4", .of_match_table = of_match_ptr(msm_dbm_1_4_id_table), diff --git a/drivers/usb/dwc3/dbm-1_5.c b/drivers/usb/dwc3/dbm-1_5.c index 2a3609ef6ffb6..7f6c5f0bfe15d 100644 --- a/drivers/usb/dwc3/dbm-1_5.c +++ b/drivers/usb/dwc3/dbm-1_5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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 @@ -387,7 +387,6 @@ static int msm_dbm_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; struct dbm *dbm; struct resource *res; - int ret = 0; dbm_data = devm_kzalloc(dev, sizeof(*dbm_data), GFP_KERNEL); if (!dbm_data) @@ -397,24 +396,21 @@ static int msm_dbm_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "missing memory base resource\n"); - ret = -ENODEV; - goto free_dbm_data; + return -ENODEV; } dbm_data->base = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); if (!dbm_data->base) { dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto free_dbm_data; + return -ENOMEM; } dbm = devm_kzalloc(dev, sizeof(*dbm), GFP_KERNEL); if (!dbm) { dev_err(&pdev->dev, "not enough memory\n"); - ret = -ENOMEM; - goto free_dbm_data; + return -ENOMEM; } dbm_data->dbm_reset_ep_after_lpm = of_property_read_bool(node, @@ -437,20 +433,6 @@ static int msm_dbm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dbm); return usb_add_dbm(dbm); - -free_dbm_data: - kfree(dbm_data); - return ret; -} - -static int msm_dbm_remove(struct platform_device *pdev) -{ - struct dbm *dbm = platform_get_drvdata(pdev); - - kfree(dbm); - kfree(dbm_data); - - return 0; } static const struct of_device_id msm_dbm_1_5_id_table[] = { @@ -463,7 +445,6 @@ MODULE_DEVICE_TABLE(of, msm_dbm_1_5_id_table); static struct platform_driver msm_dbm_driver = { .probe = msm_dbm_probe, - .remove = msm_dbm_remove, .driver = { .name = "msm-usb-dbm-1-5", .of_match_table = of_match_ptr(msm_dbm_1_5_id_table), From 0a2a70425d81f05bf8b8cacec85bcb120c69e6ad Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 29 Apr 2014 23:40:14 -0400 Subject: [PATCH 214/296] new helper: dentry_free() The part of old d_free() is that dealt with actual freeing of dentry. Taken out of dentry_kill() into a separate function. Git-repo: https://github.com/torvalds/linux.git Git-commit: b4f0354e968f5fabd39bc85b99fedae4a97589fe Change-Id: I4b16554b6f6acc558d299ca3282eebf93612d8a9 Signed-off-by: Al Viro Signed-off-by: Ankit Jain Signed-off-by: anupritaisno1 --- fs/dcache.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index dcb875bdd1ab3..cdfe02554c786 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -220,6 +220,15 @@ static void __d_free(struct rcu_head *head) kmem_cache_free(dentry_cache, dentry); } +static void dentry_free(struct dentry *dentry) +{ + /* if dentry was never visible to RCU, immediate free is OK */ + if (!(dentry->d_flags & DCACHE_RCUACCESS)) + __d_free(&dentry->d_u.d_rcu); + else + call_rcu(&dentry->d_u.d_rcu, __d_free); +} + /* * no locks, please. */ @@ -231,11 +240,7 @@ static void d_free(struct dentry *dentry) if (dentry->d_op && dentry->d_op->d_release) dentry->d_op->d_release(dentry); - /* if dentry was never visible to RCU, immediate free is OK */ - if (!(dentry->d_flags & DCACHE_RCUACCESS)) - __d_free(&dentry->d_u.d_rcu); - else - call_rcu(&dentry->d_u.d_rcu, __d_free); + dentry_free(dentry); } void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry) From aa115c0d84829a122bd13a98f7ec99ae9a1d5f1a Mon Sep 17 00:00:00 2001 From: "Mugata, Sreenivasa Rao" Date: Sun, 11 Mar 2018 12:37:30 +0530 Subject: [PATCH 215/296] Allow sharing external names after __d_move() * external dentry names get a small structure prepended to them (struct external_name). * it contains an atomic refcount, matching the number of struct dentry instances that have ->d_name.name pointing to that external name. The first thing free_dentry() does is decrementing refcount of external name, so the instances that are between the call of free_dentry() and RCU-delayed actual freeing do not contribute. * __d_move(x, y, false) makes the name of x equal to the name of y, external or not. If y has an external name, extra reference is grabbed and put into x->d_name.name. If x used to have an external name, the reference to the old name is dropped and, should it reach zero, freeing is scheduled via kfree_rcu(). * free_dentry() in dentry with external name decrements the refcount of that name and, should it reach zero, does RCU-delayed call that will free both the dentry and external name. Otherwise it does what it used to do, except that __d_free() doesn't even look at ->d_name.name; it simply frees the dentry. All non-RCU accesses to dentry external name are safe wrt freeing since they all should happen before free_dentry() is called. RCU accesses might run into a dentry seen by free_dentry() or into an old name that got already dropped by __d_move(); however, in both cases dentry must have been alive and refer to that name at some point after we'd done rcu_read_lock(), which means that any freeing must be still pending. Git-repo: https://github.com/torvalds/linux.git Git-commit: 8d85b4845a668d9a72649005c5aa932657311bd4 Change-Id: I1fa645f0e2eba6e8485daa8593600f933a5342c9 Signed-off-by: Al Viro Signed-off-by: Ankit Jain Signed-off-by: Mugata, Sreenivasa Rao Signed-off-by: anupritaisno1 --- fs/dcache.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index cdfe02554c786..aa42c7930c14d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -211,17 +211,42 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c return dentry_string_cmp(cs, ct, tcount); } +struct external_name { + union { + atomic_t count; + struct rcu_head head; + } u; + unsigned char name[]; +}; + +static inline struct external_name *external_name(struct dentry *dentry) +{ + return container_of(dentry->d_name.name, struct external_name, name[0]); +} + static void __d_free(struct rcu_head *head) { struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); - if (dname_external(dentry)) - kfree(dentry->d_name.name); kmem_cache_free(dentry_cache, dentry); } +static void __d_free_external(struct rcu_head *head) +{ + struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); + kfree(external_name(dentry)); + kmem_cache_free(dentry_cache, dentry); +} + static void dentry_free(struct dentry *dentry) { + if (unlikely(dname_external(dentry))) { + struct external_name *p = external_name(dentry); + if (likely(atomic_dec_and_test(&p->u.count))) { + call_rcu(&dentry->d_u.d_rcu, __d_free_external); + return; + } + } /* if dentry was never visible to RCU, immediate free is OK */ if (!(dentry->d_flags & DCACHE_RCUACCESS)) __d_free(&dentry->d_u.d_rcu); @@ -1300,11 +1325,14 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) */ dentry->d_iname[DNAME_INLINE_LEN-1] = 0; if (name->len > DNAME_INLINE_LEN-1) { - dname = kmalloc(name->len + 1, GFP_KERNEL); - if (!dname) { + size_t size = offsetof(struct external_name, name[1]); + struct external_name *p = kmalloc(size + name->len, GFP_KERNEL); + if (!p) { kmem_cache_free(dentry_cache, dentry); return NULL; } + atomic_set(&p->u.count, 1); + dname = p->name; } else { dname = dentry->d_iname; } @@ -2196,8 +2224,8 @@ EXPORT_SYMBOL(dentry_update_name_case); static void switch_names(struct dentry *dentry, struct dentry *target) { - if (dname_external(target)) { - if (dname_external(dentry)) { + if (unlikely(dname_external(target))) { + if (unlikely(dname_external(dentry))) { /* * Both external: swap the pointers */ @@ -2213,7 +2241,7 @@ static void switch_names(struct dentry *dentry, struct dentry *target) target->d_name.name = target->d_iname; } } else { - if (dname_external(dentry)) { + if (unlikely(dname_external(dentry))) { /* * dentry:external, target:internal. Give dentry's * storage to target and make dentry internal @@ -2235,6 +2263,25 @@ static void switch_names(struct dentry *dentry, struct dentry *target) swap(dentry->d_name.len, target->d_name.len); } +static void copy_name(struct dentry *dentry, struct dentry *target) +{ + struct external_name *old_name = NULL; + if (unlikely(dname_external(dentry))) + old_name = external_name(dentry); + if (unlikely(dname_external(target))) { + atomic_inc(&external_name(target)->u.count); + dentry->d_name = target->d_name; + } else { + memcpy(dentry->d_iname, target->d_name.name, + target->d_name.len + 1); + dentry->d_name.name = dentry->d_iname; + dentry->d_name.len = target->d_name.len; + } + if (old_name && likely(atomic_dec_and_test(&old_name->u.count))) + kfree_rcu(old_name, u.head); +} + + static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) { /* @@ -2321,7 +2368,7 @@ static void __d_move(struct dentry * dentry, struct dentry * target) list_del(&target->d_child); /* Switch the names.. */ - switch_names(dentry, target); + copy_name(dentry, target); swap(dentry->d_name.hash, target->d_name.hash); /* ... and switch the parents */ From 18965a21c94961963bb902080afb466b64ced561 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Thu, 9 Oct 2014 15:29:38 -0700 Subject: [PATCH 216/296] fs/buffer.c: increase the buffer-head per-CPU LRU size Increase the buffer-head per-CPU LRU size to allow efficient filesystem operations that access many blocks for each transaction. For example, creating a file in a large ext4 directory with quota enabled will access multiple buffer heads and will overflow the LRU at the default 8-block LRU size: * parent directory inode table block (ctime, nlinks for subdirs) * new inode bitmap * inode table block * 2 quota blocks * directory leaf block (not reused, but pollutes one cache entry) * 2 levels htree blocks (only one is reused, other pollutes cache) * 2 levels indirect/index blocks (only one is reused) The buffer-head per-CPU LRU size is raised to 16, as it shows in metadata performance benchmarks up to 10% gain for create, 4% for lookup and 7% for destroy. Change-Id: Ia8e4c430886b29fca32b7d4319365088cc2ca9d1 Signed-off-by: Liang Zhen Signed-off-by: Andreas Dilger Signed-off-by: Sebastien Buisson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: anupritaisno1 --- fs/buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/buffer.c b/fs/buffer.c index 963b348199007..c36b69324ada5 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1263,7 +1263,7 @@ static struct buffer_head *__bread_slow(struct buffer_head *bh) * a local interrupt disable for that. */ -#define BH_LRU_SIZE 8 +#define BH_LRU_SIZE 16 struct bh_lru { struct buffer_head *bhs[BH_LRU_SIZE]; From b4dfa526c7f5b3627666d8d5eebe6fad6b05a1cb Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 12 Dec 2013 09:53:51 -0800 Subject: [PATCH 217/296] futex: move user address verification up to common code When debugging the read-only hugepage case, I was confused by the fact that get_futex_key() did an access_ok() only for the non-shared futex case, since the user address checking really isn't in any way specific to the private key handling. Now, it turns out that the shared key handling does effectively do the equivalent checks inside get_user_pages_fast() (it doesn't actually check the address range on x86, but does check the page protections for being a user page). So it wasn't actually a bug, but the fact that we treat the address differently for private and shared futexes threw me for a loop. Just move the check up, so that it gets done for both cases. Also, use the 'rw' parameter for the type, even if it doesn't actually matter any more (it's a historical artifact of the old racy i386 "page faults from kernel space don't check write protections"). Change-Id: I70a366e4e392e917dce1ac19d66dfaea8984939b Cc: Thomas Gleixner Signed-off-by: Linus Torvalds Signed-off-by: anupritaisno1 --- kernel/futex.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 191a280d1275e..800994c0b388f 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -254,6 +254,9 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) return -EINVAL; address -= key->both.offset; + if (unlikely(!access_ok(rw, uaddr, sizeof(u32)))) + return -EFAULT; + /* * PROCESS_PRIVATE futexes are fast. * As the mm cannot disappear under us and the 'key' only needs @@ -262,8 +265,6 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) * but access_ok() should be faster than find_vma() */ if (!fshared) { - if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))) - return -EFAULT; key->private.mm = mm; key->private.address = address; get_futex_key_refs(key); From 7706ca668355058323ad764af8eb36e04cf330fd Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Tue, 31 Jul 2018 21:40:38 +0100 Subject: [PATCH 218/296] haruhi_defconfig: update defconfig for UID_SYS_STAT Signed-off-by: anupritaisno1 --- arch/arm64/configs/haruhi_defconfig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/configs/haruhi_defconfig b/arch/arm64/configs/haruhi_defconfig index f8127a95cb7f7..c718ade63122b 100644 --- a/arch/arm64/configs/haruhi_defconfig +++ b/arch/arm64/configs/haruhi_defconfig @@ -224,7 +224,6 @@ CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_UID_STAT=y -CONFIG_UID_CPUTIME=y CONFIG_QSEECOM=y CONFIG_TI_DRV2667=y CONFIG_TI_DRV2605=y @@ -586,6 +585,11 @@ CONFIG_TRUSTY_VIRTIO=y CONFIG_TRUSTY_VIRTIO_IPC=y CONFIG_PROC_DEVICETREE=y CONFIG_SLIMBUS_MSM_CTRL=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_UID_SYS_STATS=y # CONFIG_EXFAT_DEBUG is not set CONFIG_QUOTA=y CONFIG_QUOTACTL=y From a141c16c924ee06159d95dde95eebb632dba882d Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Tue, 31 Jul 2018 21:52:05 +0100 Subject: [PATCH 219/296] Revert "haruhi_defconfig: update defconfig for UID_SYS_STAT" This reverts commit 7706ca668355058323ad764af8eb36e04cf330fd. Signed-off-by: anupritaisno1 --- arch/arm64/configs/haruhi_defconfig | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/arm64/configs/haruhi_defconfig b/arch/arm64/configs/haruhi_defconfig index c718ade63122b..f8127a95cb7f7 100644 --- a/arch/arm64/configs/haruhi_defconfig +++ b/arch/arm64/configs/haruhi_defconfig @@ -224,6 +224,7 @@ CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_UID_STAT=y +CONFIG_UID_CPUTIME=y CONFIG_QSEECOM=y CONFIG_TI_DRV2667=y CONFIG_TI_DRV2605=y @@ -585,11 +586,6 @@ CONFIG_TRUSTY_VIRTIO=y CONFIG_TRUSTY_VIRTIO_IPC=y CONFIG_PROC_DEVICETREE=y CONFIG_SLIMBUS_MSM_CTRL=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_XACCT=y -CONFIG_TASK_IO_ACCOUNTING=y -CONFIG_UID_SYS_STATS=y # CONFIG_EXFAT_DEBUG is not set CONFIG_QUOTA=y CONFIG_QUOTACTL=y From 30e460f35e8dfb12bdfb632c4409ff60e5d21255 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 19 Apr 2018 13:41:16 +0530 Subject: [PATCH 220/296] msm: ipa: rmnet: Make code changes with respect to CR#2046006 Check for CAP_NET_ADMIN capability of the user space application who tries to access rmnet driver IOCTL. Bug: 36367253 Change-Id: If6bb4b54659306c5103b5e34bf02c7234c851e0a CRs-Fixed: 2226355 Signed-off-by: Mohammed Javid Signed-off-by: anupritaisno1 # Conflicts: # drivers/platform/msm/ipa/rmnet_ipa.c --- drivers/platform/msm/ipa/rmnet_ipa.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/msm/ipa/rmnet_ipa.c b/drivers/platform/msm/ipa/rmnet_ipa.c index 658ad89aa3607..8a7d2f1c62401 100644 --- a/drivers/platform/msm/ipa/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/rmnet_ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-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 @@ -1173,6 +1173,8 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Extended IOCTLs */ case RMNET_IOCTL_EXTENDED: + if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN)) + return -EPERM; IPAWANDBG("get ioctl: RMNET_IOCTL_EXTENDED\n"); if (copy_from_user(&extend_ioctl_data, (u8 *)ifr->ifr_ifru.ifru_data, From d62916c4bfbfc61f5284466d5c282fd4ea23c79a Mon Sep 17 00:00:00 2001 From: Sreelakshmi Gownipalli Date: Tue, 23 Jan 2018 13:11:41 -0800 Subject: [PATCH 221/296] diag: Protect the decrement of number of diag clients In diagchar_open() protect the decrement of number of diag clients so that there will be no race conditions while reading the value from other functions. Bug: 79421261 Change-Id: I0e2fb5331eec9c7bba39e7d881b69559256833a3 Signed-off-by: Sreelakshmi Gownipalli Signed-off-by: anupritaisno1 --- drivers/char/diag/diagchar_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index d6196556d9de0..58eb82c537e9b 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2016, 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 @@ -322,9 +323,9 @@ static int diagchar_open(struct inode *inode, struct file *file) return -ENOMEM; fail: - mutex_unlock(&driver->diagchar_mutex); driver->num_clients--; - pr_alert("diag: Insufficient memory for new client"); + mutex_unlock(&driver->diagchar_mutex); + pr_err_ratelimited("diag: Insufficient memory for new client"); return -ENOMEM; } From e7df470ed61bd9edcdb69f0dc2b14ba29de06a66 Mon Sep 17 00:00:00 2001 From: Harsh Sahu Date: Fri, 23 Mar 2018 00:15:52 -0700 Subject: [PATCH 222/296] msm: mdss: check buffer size before writing to user buffer Check the number of bytes to copy against the size of the user buffer before copy to user to avoid buffer overflow. Bug: 79422277 Change-Id: Icdd3d4e755deca19fa431e903620bd9e4c701c89 Signed-off-by: Harsh Sahu Signed-off-by: Siqi Lin Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_debug_xlog.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_debug_xlog.c b/drivers/video/msm/mdss/mdss_debug_xlog.c index 1fb2d7d06fb31..17b09e0cbbdd6 100644 --- a/drivers/video/msm/mdss/mdss_debug_xlog.c +++ b/drivers/video/msm/mdss/mdss_debug_xlog.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, 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 @@ -560,6 +560,11 @@ static ssize_t mdss_xlog_dump_read(struct file *file, char __user *buff, if (__mdss_xlog_dump_calc_range()) { len = mdss_xlog_dump_entry(xlog_buf, MDSS_XLOG_BUF_MAX); + if (len < 0 || len > count) { + pr_err("len is more than the size of user buffer\n"); + return 0; + } + if (copy_to_user(buff, xlog_buf, len)) return -EFAULT; *ppos += len; From cdd01646cec7c1fb58b55b3ee9ee0d28073005f2 Mon Sep 17 00:00:00 2001 From: annamraj Date: Wed, 11 Apr 2018 10:42:13 +0530 Subject: [PATCH 223/296] msm: camera: Fix for Possible information leak issue Fix for possible information leak issue because of unintialised variable Which can be accesed from userspace in camera fd driver Bug: 73889358 Signed-off-by: annamraj Change-Id: I4552c4829e9532d848e46fd123316b26105e310e Signed-off-by: anupritaisno1 --- drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c index b4616f60dce12..7dd00e1c88255 100644 --- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c @@ -1,4 +1,4 @@ -/* 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 @@ -384,7 +384,7 @@ static int msm_fd_open(struct file *file) ctx->mem_pool.fd_device = ctx->fd_device; ctx->mem_pool.domain_num = ctx->fd_device->iommu_domain_num; - ctx->stats = vmalloc(sizeof(*ctx->stats) * MSM_FD_MAX_RESULT_BUFS); + ctx->stats = vzalloc(sizeof(*ctx->stats) * MSM_FD_MAX_RESULT_BUFS); if (!ctx->stats) { dev_err(device->dev, "No memory for face statistics\n"); ret = -ENOMEM; From 9970f235628375e042b13fbb175a402110599d45 Mon Sep 17 00:00:00 2001 From: Mahesh Sivasubramanian Date: Wed, 7 Mar 2018 16:00:07 -0700 Subject: [PATCH 224/296] drivers: qcom: lpm-stats: Fix undefined access error In cleanup_stats(), a freed memory pointer pos might be accessed for list traversal. Switch to using _safe() variant of the list API to prevent undefined accesses. Bug: 79421260 Change-Id: I7d068cb7813ccb9bfdbcab4646b4ec890145828a Signed-off-by: Mahesh Sivasubramanian Signed-off-by: anupritaisno1 --- drivers/power/qcom/lpm-stats.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/power/qcom/lpm-stats.c b/drivers/power/qcom/lpm-stats.c index 321e13c2b7ea3..f4f22007b4cbe 100644 --- a/drivers/power/qcom/lpm-stats.c +++ b/drivers/power/qcom/lpm-stats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -590,11 +590,14 @@ static void cleanup_stats(struct lpm_stats *stats) { struct list_head *centry = NULL; struct lpm_stats *pos = NULL; + struct lpm_stats *n = NULL; centry = &stats->child; - list_for_each_entry_reverse(pos, centry, sibling) { - if (!list_empty(&pos->child)) + list_for_each_entry_safe_reverse(pos, n, centry, sibling) { + if (!list_empty(&pos->child)) { cleanup_stats(pos); + continue; + } list_del_init(&pos->child); From 4dd65220bb84d8ce7a4350766b6d02d366d4eaef Mon Sep 17 00:00:00 2001 From: flar2 Date: Thu, 13 Aug 2015 23:17:26 -0400 Subject: [PATCH 225/296] mmc: clk_scaling: set scale_down_in_low_wr_load to 1 by default As per Qualcomm: The default value for it is "0". In case customers want to gain performance over power they should set it to "1" Signed-off-by: anupritaisno1 --- drivers/mmc/core/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 671473247496d..1ec2910c1df4f 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -931,7 +931,7 @@ int mmc_add_host(struct mmc_host *host) host->clk_scaling.up_threshold = 35; host->clk_scaling.down_threshold = 5; host->clk_scaling.polling_delay_ms = 100; - host->clk_scaling.scale_down_in_low_wr_load = false; + host->clk_scaling.scale_down_in_low_wr_load = true; err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp); if (err) From 34c707bf5b27d827968b40eee253a9fbff9f5cd6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 14 May 2018 11:56:36 +0100 Subject: [PATCH 226/296] ARM: spectre: add Kconfig symbol for CPUs vulnerable to Spectre Add a Kconfig symbol for CPUs which are vulnerable to the Spectre attacks. Change-Id: I63aeb80cb82f9de4ddbb3bf83fefd5b0ce4237ba Signed-off-by: Russell King Reviewed-by: Florian Fainelli Boot-tested-by: Tony Lindgren Reviewed-by: Tony Lindgren Acked-by: Marc Zyngier Signed-off-by: anupritaisno1 --- arch/arm/mm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index a5d9077712888..4381c9b52d078 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -395,6 +395,7 @@ config CPU_V7 select CPU_CP15_MMU select CPU_HAS_ASID if MMU select CPU_PABRT_V7 + select CPU_SPECTRE if MMU select CPU_TLB_V7 if MMU config CPU_THUMBONLY @@ -772,6 +773,9 @@ config CPU_BPREDICT_DISABLE help Say Y here to disable branch prediction. If unsure, say N. +config CPU_SPECTRE + bool + config TLS_REG_EMUL bool select NEED_KUSER_HELPERS From 05f74d5fd82202487640def9f1511f9eebbf3af7 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Fri, 10 Nov 2017 15:30:27 -0800 Subject: [PATCH 227/296] 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 Signed-off-by: Siqi Lin (cherry picked from commit cfe3642b4f6541dffb4899963c61770f54b674ee) Signed-off-by: anupritaisno1 --- drivers/android/binder.c | 48 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index e2e2a99ff3bd1..84fc783770c71 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -328,9 +328,8 @@ struct binder_buffer { }; 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, }; struct binder_proc { @@ -343,7 +342,6 @@ struct binder_proc { struct vm_area_struct *vma; struct mm_struct *vma_vm_mm; struct task_struct *tsk; - struct files_struct *files; struct hlist_node deferred_work_node; int deferred_work; void *buffer; @@ -416,18 +414,26 @@ struct binder_transaction { static void binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer); +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); @@ -436,6 +442,8 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) ret = __alloc_fd(files, 0, rlim_cur, flags); preempt_disable(); +err: + put_files_struct(files); return ret; } @@ -445,9 +453,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) { + struct files_struct *files = binder_get_files_struct(proc); + + if (files) { preempt_enable_no_resched(); - __fd_install(proc->files, fd, file); + __fd_install(files, fd, file); + put_files_struct(files); preempt_disable(); } } @@ -457,18 +468,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; } @@ -3489,7 +3502,6 @@ static void binder_vma_close(struct vm_area_struct *vma) (unsigned long)pgprot_val(vma->vm_page_prot)); proc->vma = NULL; proc->vma_vm_mm = NULL; - binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); } static int binder_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) @@ -3595,7 +3607,6 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) binder_insert_free_buffer(proc, buffer); proc->free_async_space = proc->buffer_size / 2; barrier(); - proc->files = get_files_struct(current); proc->vma = vma; proc->vma_vm_mm = vma->vm_mm; @@ -3768,7 +3779,6 @@ static void binder_deferred_release(struct binder_proc *proc) active_transactions, page_count; BUG_ON(proc->vma); - BUG_ON(proc->files); hlist_del(&proc->proc_node); @@ -3878,7 +3888,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; struct binder_context *context = container_of(work, struct binder_context, deferred_work); @@ -3904,13 +3913,6 @@ static void binder_deferred_func(struct work_struct *work) } mutex_unlock(&context->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); @@ -3920,8 +3922,6 @@ static void binder_deferred_func(struct work_struct *work) trace_binder_unlock(__func__); mutex_unlock(&context->binder_main_lock); preempt_enable_no_resched(); - if (files) - put_files_struct(files); } while (proc); } From 3dad9c360a32ceb1329179e02627eb5e763e4fac Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 11 May 2018 11:15:29 +0100 Subject: [PATCH 228/296] ARM: spectre-v1: add speculation barrier (csdb) macros Add assembly and C macros for the new CSDB instruction. Change-Id: Iff3490a0ebc290edf22128eba9e367dc5134fb3e Signed-off-by: Russell King Acked-by: Mark Rutland Boot-tested-by: Tony Lindgren Reviewed-by: Tony Lindgren Signed-off-by: anupritaisno1 --- arch/arm/include/asm/assembler.h | 8 ++++++++ arch/arm/include/asm/barrier.h | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 56cff4b6549c2..9d810e22f90b7 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -382,6 +382,14 @@ THUMB( orr \reg , \reg , #PSR_T_BIT ) .size \name , . - \name .endm + .macro csdb +#ifdef CONFIG_THUMB2_KERNEL + .inst.w 0xf3af8014 +#else + .inst 0xe320f014 +#endif + .endm + .macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req #ifndef CONFIG_CPU_USE_DOMAINS adds \tmp, \addr, #\size - 1 diff --git a/arch/arm/include/asm/barrier.h b/arch/arm/include/asm/barrier.h index c6a3e73a6e240..3f34aa9cfb373 100644 --- a/arch/arm/include/asm/barrier.h +++ b/arch/arm/include/asm/barrier.h @@ -17,6 +17,12 @@ #define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory") #define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory") #define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory") +#ifdef CONFIG_THUMB2_KERNEL +#define CSDB ".inst.w 0xf3af8014" +#else +#define CSDB ".inst 0xe320f014" +#endif +#define csdb() __asm__ __volatile__(CSDB : : : "memory") #elif defined(CONFIG_CPU_XSC3) || __LINUX_ARM_ARCH__ == 6 #define isb(x) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" \ : : "r" (0) : "memory") @@ -37,6 +43,13 @@ #define dmb(x) __asm__ __volatile__ ("" : : : "memory") #endif +#ifndef CSDB +#define CSDB +#endif +#ifndef csdb +#define csdb() +#endif + #ifdef CONFIG_ARCH_HAS_BARRIERS #include #elif defined(CONFIG_ARM_DMA_MEM_BUFFERABLE) || defined(CONFIG_SMP) From 8a3cb6932c39bc8aa82954f4022d50c571e9daa0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 11 May 2018 15:06:58 +0100 Subject: [PATCH 229/296] ARM: spectre-v1: add array_index_mask_nospec() implementation Add an implementation of the array_index_mask_nospec() function for mitigating Spectre variant 1 throughout the kernel. Change-Id: I66d16e24b4725df0bf70f53c91a34be9f26fba98 Signed-off-by: Russell King Acked-by: Mark Rutland Boot-tested-by: Tony Lindgren Reviewed-by: Tony Lindgren Signed-off-by: anupritaisno1 --- arch/arm/include/asm/barrier.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/arm/include/asm/barrier.h b/arch/arm/include/asm/barrier.h index 3f34aa9cfb373..a7bed59073df6 100644 --- a/arch/arm/include/asm/barrier.h +++ b/arch/arm/include/asm/barrier.h @@ -95,5 +95,24 @@ do { \ #define smp_mb__before_atomic() smp_mb() #define smp_mb__after_atomic() smp_mb() +#ifdef CONFIG_CPU_SPECTRE +static inline unsigned long array_index_mask_nospec(unsigned long idx, + unsigned long sz) +{ + unsigned long mask; + + asm volatile( + "cmp %1, %2\n" + " sbc %0, %1, %1\n" + CSDB + : "=r" (mask) + : "r" (idx), "Ir" (sz) + : "cc"); + + return mask; +} +#define array_index_mask_nospec array_index_mask_nospec +#endif + #endif /* !__ASSEMBLY__ */ #endif /* __ASM_BARRIER_H */ From a43d2bb538b31ddcfc6098377a077f8101561063 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 11 May 2018 11:16:22 +0100 Subject: [PATCH 230/296] ARM: spectre-v1: fix syscall entry Prevent speculation at the syscall table decoding by clamping the index used to zero on invalid system call numbers, and using the csdb speculative barrier. Change-Id: I59151ce75a3887d88eed852187f05013e665a7cb Signed-off-by: Russell King Acked-by: Mark Rutland Boot-tested-by: Tony Lindgren Reviewed-by: Tony Lindgren Signed-off-by: anupritaisno1 --- arch/arm/kernel/entry-common.S | 18 +++++++----------- arch/arm/kernel/entry-header.S | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index f2646934c7142..eb5715e6b9cf4 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -435,9 +435,7 @@ local_restart: tst r10, #_TIF_SYSCALL_WORK @ are we tracing syscalls? bne __sys_trace - cmp scno, #NR_syscalls @ check upper syscall limit - adr lr, BSYM(ret_fast_syscall) @ return address - ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine + invoke_syscall tbl, scno, r10, ret_fast_syscall add r1, sp, #S_OFF 2: mov why, #0 @ no longer a real syscall @@ -470,14 +468,8 @@ __sys_trace: mov r1, scno add r0, sp, #S_OFF bl syscall_trace_enter - - adr lr, BSYM(__sys_trace_return) @ return address - mov scno, r0 @ syscall number (possibly new) - add r1, sp, #S_R0 + S_OFF @ pointer to regs - cmp scno, #NR_syscalls @ check upper syscall limit - ldmccia r1, {r0 - r6} @ have to reload r0 - r6 - stmccia sp, {r4, r5} @ and update the stack args - ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine + mov scno, r0 + invoke_syscall tbl, scno, r10, __sys_trace_return, reload=1 cmp scno, #-1 @ skip the syscall? bne 2b add sp, sp, #S_OFF @ restore stack @@ -523,6 +515,10 @@ sys_syscall: bic scno, r0, #__NR_OABI_SYSCALL_BASE cmp scno, #__NR_syscall - __NR_SYSCALL_BASE cmpne scno, #NR_syscalls @ check range +#ifdef CONFIG_CPU_SPECTRE + movhs scno, #0 + csdb +#endif stmloia sp, {r5, r6} @ shuffle args movlo r0, r1 movlo r1, r2 diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 160f3376ba6da..eb7c10f01d029 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -225,6 +225,31 @@ #endif .endm + .macro invoke_syscall, table, nr, tmp, ret, reload=0 +#ifdef CONFIG_CPU_SPECTRE + mov \tmp, \nr + cmp \tmp, #NR_syscalls @ check upper syscall limit + movcs \tmp, #0 + csdb + adr lr, BSYM(\ret) @ return address + .if \reload + add r1, sp, #S_R0 + S_OFF @ pointer to regs + ldmccia r1, {r0 - r6} @ reload r0-r6 + stmccia sp, {r4, r5} @ update stack arguments + .endif + ldrcc pc, [\table, \tmp, lsl #2] @ call sys_* routine +#else + cmp \nr, #NR_syscalls @ check upper syscall limit + adr lr, BSYM(\ret) @ return address + .if \reload + add r1, sp, #S_R0 + S_OFF @ pointer to regs + ldmccia r1, {r0 - r6} @ reload r0-r6 + stmccia sp, {r4, r5} @ update stack arguments + .endif + ldrcc pc, [\table, \nr, lsl #2] @ call sys_* routine +#endif + .endm + /* * These are the registers used in the syscall handler, and allow us to * have in theory up to 7 arguments to a function - r0 to r6. From 2f4f14b5d76a35a9f74d91106b886fd0ac6817e8 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 6 Aug 2018 13:06:00 -0700 Subject: [PATCH 231/296] ANDROID: Revert "net: increase fragment memory usage limits" This reverts commit c2a936600f78aea00d3312ea4b66a79a4619f9b4. Bug: 111983486 Change-Id: Ibc7a3076d7ec928dac27c2fd2d1bdaff6cb8c349 Signed-off-by: Greg Hackmann Signed-off-by: anupritaisno1 --- include/net/ipv6.h | 4 ++-- net/ipv4/ip_fragment.c | 22 +++++++--------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 1c581b99c64b4..a3e42dfd69417 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -322,8 +322,8 @@ static inline int ip6_frag_mem(struct net *net) } #endif -#define IPV6_FRAG_HIGH_THRESH (4 * 1024*1024) /* 4194304 */ -#define IPV6_FRAG_LOW_THRESH (3 * 1024*1024) /* 3145728 */ +#define IPV6_FRAG_HIGH_THRESH (256 * 1024) /* 262144 */ +#define IPV6_FRAG_LOW_THRESH (192 * 1024) /* 196608 */ #define IPV6_FRAG_TIMEOUT (60 * HZ) /* 60 seconds */ extern int __ipv6_addr_type(const struct in6_addr *addr); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 04c7e46180087..989201a3b4d4e 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -831,22 +831,14 @@ static inline void ip4_frags_ctl_register(void) static int __net_init ipv4_frags_init_net(struct net *net) { - /* Fragment cache limits. - * - * The fragment memory accounting code, (tries to) account for - * the real memory usage, by measuring both the size of frag - * queue struct (inet_frag_queue (ipv4:ipq/ipv6:frag_queue)) - * and the SKB's truesize. - * - * A 64K fragment consumes 129736 bytes (44*2944)+200 - * (1500 truesize == 2944, sizeof(struct ipq) == 200) - * - * We will commit 4MB at one time. Should we cross that limit - * we will prune down to 3MB, making room for approx 8 big 64K - * fragments 8x128k. + /* + * Fragment cache limits. We will commit 256K at one time. Should we + * cross that limit we will prune down to 192K. This should cope with + * even the most extreme cases without allowing an attacker to + * measurably harm machine performance. */ - net->ipv4.frags.high_thresh = 4 * 1024 * 1024; - net->ipv4.frags.low_thresh = 3 * 1024 * 1024; + net->ipv4.frags.high_thresh = 256 * 1024; + net->ipv4.frags.low_thresh = 192 * 1024; /* * Important NOTE! Fragment queue must be destroyed before MSL expires. * RFC791 is wrong proposing to prolongate timer each fragment arrival From 164003de616675bd9ce77288b4619f4277fdd04c Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 15 Aug 2018 22:48:58 +0100 Subject: [PATCH 232/296] net/wireguard: update wireguard importer (squashed) Signed-off-by: anupritaisno1 --- scripts/fetch-latest-wireguard.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/fetch-latest-wireguard.sh b/scripts/fetch-latest-wireguard.sh index dcc25dec71fbf..dd6ed4db3ce8f 100755 --- a/scripts/fetch-latest-wireguard.sh +++ b/scripts/fetch-latest-wireguard.sh @@ -1,18 +1,28 @@ #!/bin/bash set -e -USER_AGENT="WireGuard-AndroidROMBuild/0.1 ($(uname -a))" +USER_AGENT="WireGuard-AndroidROMBuild/0.2 ($(uname -a))" + +exec 9>.wireguard-fetch-lock +flock -n 9 || exit 0 [[ $(( $(date +%s) - $(stat -c %Y "net/wireguard/.check" 2>/dev/null || echo 0) )) -gt 86400 ]] || exit 0 -[[ $(curl -A "$USER_AGENT" -LSs https://git.zx2c4.com/WireGuard/refs/) =~ snapshot/WireGuard-([0-9.]+)\.tar\.xz ]] +while read -r distro package version _; do + if [[ $distro == upstream && $package == kmodtools ]]; then + VERSION="$version" + break + fi +done < <(curl -A "$USER_AGENT" -LSs https://build.wireguard.com/distros.txt) + +[[ -n $VERSION ]] -if [[ -f net/wireguard/version.h && $(< net/wireguard/version.h) == *${BASH_REMATCH[1]}* ]]; then +if [[ -f net/wireguard/version.h && $(< net/wireguard/version.h) == *$VERSION* ]]; then touch net/wireguard/.check exit 0 fi rm -rf net/wireguard mkdir -p net/wireguard -curl -A "$USER_AGENT" -LsS "https://git.zx2c4.com/WireGuard/snapshot/WireGuard-${BASH_REMATCH[1]}.tar.xz" | tar -C "net/wireguard" -xJf - --strip-components=2 "WireGuard-${BASH_REMATCH[1]}/src" +curl -A "$USER_AGENT" -LsS "https://git.zx2c4.com/WireGuard/snapshot/WireGuard-$VERSION.tar.xz" | tar -C "net/wireguard" -xJf - --strip-components=2 "WireGuard-$VERSION/src" sed -i 's/tristate/bool/;s/default m/default y/;' net/wireguard/Kconfig touch net/wireguard/.check From c3cddb287b7d303da8592d5d944f0e505095293b Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Wed, 15 Aug 2018 22:54:17 +0100 Subject: [PATCH 233/296] travis-ci: clone toolchain faster Signed-off-by: anupritaisno1 --- travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis.sh b/travis.sh index cde9aab28853f..ef2c92f3e08f0 100644 --- a/travis.sh +++ b/travis.sh @@ -1,4 +1,4 @@ -git clone https://bitbucket.org/anupritaisno1/aarch64-linux-gnu -b linaro +git clone https://bitbucket.org/anupritaisno1/aarch64-linux-gnu -b linaro --single-branch --depth=1 -4 aarch64-linux-gnu -j$(nproc --all); export CROSS_COMPILE="/usr/bin/ccache ./aarch64-linux-gnu/bin/aarch64-linux-gnu-" ; export ARCH=arm64; export SUBARCH=arm64; From 1607a690ae2e79874e2306da8920276d536be70a Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Tue, 21 Aug 2018 01:06:09 +0100 Subject: [PATCH 234/296] sdfat: catch up to version 2.0.8 Signed-off-by: anupritaisno1 --- fs/sdfat/statistics.c | 38 +++++++++++++++++++++++++------------- fs/sdfat/version.h | 2 +- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/fs/sdfat/statistics.c b/fs/sdfat/statistics.c index 00624df5a6364..2ae1a93fb3302 100644 --- a/fs/sdfat/statistics.c +++ b/fs/sdfat/statistics.c @@ -47,8 +47,10 @@ static struct kset *sdfat_statistics_kset; static ssize_t vfat_cl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) { - return snprintf(buff, PAGE_SIZE, "VCL_512B_I:%u,VCL_1K_I:%u,VCL_2K_I:%u," - "VCL_4K_I:%u,VCL_8K_I:%u,VCL_16K_I:%u,VCL_32K_I:%u\n", + return snprintf(buff, PAGE_SIZE, "\"VCL_512B_I\":\"%u\"," + "\"VCL_1K_I\":\"%u\",\"VCL_2K_I\":\"%u\"," + "\"VCL_4K_I\":\"%u\",\"VCL_8K_I\":\"%u\"," + "\"VCL_16K_I\":\"%u\",\"VCL_32K_I\":\"%u\"\n", statistics.clus_vfat[0], statistics.clus_vfat[1], statistics.clus_vfat[2], statistics.clus_vfat[3], statistics.clus_vfat[4], statistics.clus_vfat[5], @@ -58,10 +60,15 @@ static ssize_t vfat_cl_show(struct kobject *kobj, static ssize_t exfat_cl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) { - return snprintf(buff, PAGE_SIZE, "ECL_512B_I:%u,ECL_1K_I:%u,ECL_2K_I:%u," - "ECL_4K_I:%u,ECL_8K_I:%u,ECL_16K_I:%u,ECL_32K_I:%u,ECL_64K_I:%u," - "ECL_128K_I:%u,ECL_256K_I:%u,ECL_512K_I:%u,ECL_1M_I:%u," - "ECL_2M_I:%u,ECL_4M_I:%u,ECL_8M_I:%u,ECL_16M_I:%u,ECL_32M_I:%u\n", + return snprintf(buff, PAGE_SIZE, "\"ECL_512B_I\":\"%u\"," + "\"ECL_1K_I\":\"%u\",\"ECL_2K_I\":\"%u\"," + "\"ECL_4K_I\":\"%u\",\"ECL_8K_I\":\"%u\"," + "\"ECL_16K_I\":\"%u\",\"ECL_32K_I\":\"%u\"," + "\"ECL_64K_I\":\"%u\",\"ECL_128K_I\":\"%u\"," + "\"ECL_256K_I\":\"%u\",\"ECL_512K_I\":\"%u\"," + "\"ECL_1M_I\":\"%u\",\"ECL_2M_I\":\"%u\"," + "\"ECL_4M_I\":\"%u\",\"ECL_8M_I\":\"%u\"," + "\"ECL_16M_I\":\"%u\",\"ECL_32M_I\":\"%u\"\n", statistics.clus_exfat[0], statistics.clus_exfat[1], statistics.clus_exfat[2], statistics.clus_exfat[3], statistics.clus_exfat[4], statistics.clus_exfat[5], @@ -76,8 +83,9 @@ static ssize_t exfat_cl_show(struct kobject *kobj, static ssize_t mount_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) { - return snprintf(buff, PAGE_SIZE, "FAT12_MNT_I:%u,FAT16_MNT_I:%u,FAT32_MNT_I:%u," - "EXFAT_MNT_I:%u\n", + return snprintf(buff, PAGE_SIZE, "\"FAT12_MNT_I\":\"%u\"," + "\"FAT16_MNT_I\":\"%u\",\"FAT32_MNT_I\":\"%u\"," + "\"EXFAT_MNT_I\":\"%u\"\n", statistics.mnt_cnt[SDFAT_MNT_FAT12], statistics.mnt_cnt[SDFAT_MNT_FAT16], statistics.mnt_cnt[SDFAT_MNT_FAT32], @@ -87,8 +95,10 @@ static ssize_t mount_show(struct kobject *kobj, static ssize_t nofat_op_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) { - return snprintf(buff, PAGE_SIZE, "NOFAT_MOUNT_I:%u,NOFAT_MKDIR_I:%u,NOFAT_CREATE_I:%u," - "NOFAT_READ_I:%u,NOFAT_WRITE_I:%u,NOFAT_TRUNC_I:%u\n", + return snprintf(buff, PAGE_SIZE, "\"NOFAT_MOUNT_I\":\"%u\"," + "\"NOFAT_MKDIR_I\":\"%u\",\"NOFAT_CREATE_I\":\"%u\"," + "\"NOFAT_READ_I\":\"%u\",\"NOFAT_WRITE_I\":\"%u\"," + "\"NOFAT_TRUNC_I\":\"%u\"\n", statistics.nofat_op[SDFAT_OP_EXFAT_MNT], statistics.nofat_op[SDFAT_OP_MKDIR], statistics.nofat_op[SDFAT_OP_CREATE], @@ -100,9 +110,11 @@ static ssize_t nofat_op_show(struct kobject *kobj, static ssize_t vol_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff) { - return snprintf(buff, PAGE_SIZE, "VOL_4G_I:%u,VOL_8G_I:%u,VOL_16G_I:%u," - "VOL_32G_I:%u,VOL_64G_I:%u,VOL_128G_I:%u,VOL_256G_I:%u," - "VOL_512G_I:%u,VOL_XTB_I:%u\n", + return snprintf(buff, PAGE_SIZE, "\"VOL_4G_I\":\"%u\"," + "\"VOL_8G_I\":\"%u\",\"VOL_16G_I\":\"%u\"," + "\"VOL_32G_I\":\"%u\",\"VOL_64G_I\":\"%u\"," + "\"VOL_128G_I\":\"%u\",\"VOL_256G_I\":\"%u\"," + "\"VOL_512G_I\":\"%u\",\"VOL_XTB_I\":\"%u\"\n", statistics.vol_size[SDFAT_VOL_4G], statistics.vol_size[SDFAT_VOL_8G], statistics.vol_size[SDFAT_VOL_16G], diff --git a/fs/sdfat/version.h b/fs/sdfat/version.h index c49175c0228bb..999f052a7e928 100644 --- a/fs/sdfat/version.h +++ b/fs/sdfat/version.h @@ -22,4 +22,4 @@ /* PURPOSE : sdFAT File Manager */ /* */ /************************************************************************/ -#define SDFAT_VERSION "2.0.6-lineage" +#define SDFAT_VERSION "2.0.8-lineage" From a0fe924fdc0c3d89e5d2e7da6eb64b0958ac92fc Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Tue, 21 Aug 2018 01:49:30 +0100 Subject: [PATCH 235/296] Revert "Revert "msm8994: update idle cost values from electron kernel for ether"" This reverts commit 61d5149dad78e175e4d4c466daf4744194987262. Signed-off-by: anupritaisno1 --- arch/arm/boot/dts/qcom/msm8994.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8994.dtsi b/arch/arm/boot/dts/qcom/msm8994.dtsi index 297fdc5c32b63..b35a35d98692a 100644 --- a/arch/arm/boot/dts/qcom/msm8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm8994.dtsi @@ -348,7 +348,7 @@ vendor { 509 165 >; idle-cost-data = < - 0 0 0 0 + 56 56 56 17 >; }; CLUSTER_COST_1: cluster-cost1 { @@ -368,7 +368,7 @@ vendor { 1024 80 >; idle-cost-data = < - 0 0 0 0 + 65 65 65 24 >; }; }; From 4d8f1b1f2cf8124954881529ca24b5be9c6de409 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 20 Jul 2017 16:05:43 +0200 Subject: [PATCH 236/296] uid_sys_stats: make hash_table static Having a global variabled called "hash_table" is not a good idea, it should be static, so mark it as such. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Harsh Shandilya Signed-off-by: anupritaisno1 --- 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 2aa7246700b16..f5fd01008d0b7 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -28,7 +28,7 @@ #include #define UID_HASH_BITS 10 -DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); +static DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); static DEFINE_RT_MUTEX(uid_lock); static struct proc_dir_entry *cpu_parent; From 6c8131bdaaccc9367afd909cbf6b7699912c423d Mon Sep 17 00:00:00 2001 From: Yang Jin Date: Wed, 26 Jul 2017 12:52:22 -0700 Subject: [PATCH 237/296] uid_sys_stats: log task io with a debug flag Add a hashmap inside each uid_entry to keep track of task name and io. Task full name is a combination of thread and process name. Bug: 63739275 Change-Id: I30083b757eaef8c61e55a213a883ce8d0c9cf2b1 Signed-off-by: Yang Jin Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 320 ++++++++++++++++++++++++++++------- 1 file changed, 258 insertions(+), 62 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index f5fd01008d0b7..9e162ab1108d5 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -52,6 +52,15 @@ struct io_stats { #define UID_STATE_DEAD_TASKS 4 #define UID_STATE_SIZE 5 +#define MAX_TASK_COMM_LEN 256 + +struct task_entry { + char comm[MAX_TASK_COMM_LEN]; + pid_t pid; + struct io_stats io[UID_STATE_SIZE]; + struct hlist_node hash; +}; + struct uid_entry { uid_t uid; cputime_t utime; @@ -63,8 +72,231 @@ struct uid_entry { int state; struct io_stats io[UID_STATE_SIZE]; struct hlist_node hash; +#ifdef CONFIG_UID_SYS_STATS_DEBUG + DECLARE_HASHTABLE(task_entries, UID_HASH_BITS); +#endif }; +#if IS_ENABLED(CONFIG_TASK_IO_ACCOUNTING) +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} +#endif + +static void compute_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) +{ + /* tasks could switch to another uid group, but its io_last in the + * previous uid group could still be positive. + * therefore before each update, do an overflow check first + */ + int64_t delta; + + delta = io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->read_bytes += delta > 0 ? delta : 0; + delta = io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->write_bytes += delta > 0 ? delta : 0; + delta = io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->rchar += delta > 0 ? delta : 0; + delta = io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->wchar += delta > 0 ? delta : 0; + delta = io_curr->fsync + io_dead->fsync - io_last->fsync; + io_bucket->fsync += delta > 0 ? delta : 0; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); +} + +#ifdef CONFIG_UID_SYS_STATS_DEBUG +static void get_full_task_comm(struct task_entry *task_entry, + struct task_struct *task) +{ + int i = 0, offset = 0, len = 0; + /* save one byte for terminating null character */ + int unused_len = MAX_TASK_COMM_LEN - TASK_COMM_LEN - 1; + char buf[unused_len]; + struct mm_struct *mm = task->mm; + + /* fill the first TASK_COMM_LEN bytes with thread name */ + get_task_comm(task_entry->comm, task); + i = strlen(task_entry->comm); + while (i < TASK_COMM_LEN) + task_entry->comm[i++] = ' '; + + /* next the executable file name */ + if (mm) { + down_read(&mm->mmap_sem); + if (mm->exe_file) { + char *pathname = d_path(&mm->exe_file->f_path, buf, + unused_len); + + if (!IS_ERR(pathname)) { + len = strlcpy(task_entry->comm + i, pathname, + unused_len); + i += len; + task_entry->comm[i++] = ' '; + unused_len--; + } + } + up_read(&mm->mmap_sem); + } + unused_len -= len; + + /* fill the rest with command line argument + * replace each null or new line character + * between args in argv with whitespace */ + len = get_cmdline(task, buf, unused_len); + while (offset < len) { + if (buf[offset] != '\0' && buf[offset] != '\n') + task_entry->comm[i++] = buf[offset]; + else + task_entry->comm[i++] = ' '; + offset++; + } + + /* get rid of trailing whitespaces in case when arg is memset to + * zero before being reset in userspace + */ + while (task_entry->comm[i-1] == ' ') + i--; + task_entry->comm[i] = '\0'; +} + +static struct task_entry *find_task_entry(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct task_entry *task_entry; + + hash_for_each_possible(uid_entry->task_entries, task_entry, hash, + task->pid) { + if (task->pid == task_entry->pid) { + /* if thread name changed, update the entire command */ + int len = strnchr(task_entry->comm, ' ', TASK_COMM_LEN) + - task_entry->comm; + + if (strncmp(task_entry->comm, task->comm, len)) + get_full_task_comm(task_entry, task); + return task_entry; + } + } + return NULL; +} + +static struct task_entry *find_or_register_task(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct task_entry *task_entry; + pid_t pid = task->pid; + + task_entry = find_task_entry(uid_entry, task); + if (task_entry) + return task_entry; + + task_entry = kzalloc(sizeof(struct task_entry), GFP_ATOMIC); + if (!task_entry) + return NULL; + + get_full_task_comm(task_entry, task); + + task_entry->pid = pid; + hash_add(uid_entry->task_entries, &task_entry->hash, (unsigned int)pid); + + return task_entry; +} + +static void remove_uid_tasks(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + struct hlist_node *tmp_task; + + hash_for_each_safe(uid_entry->task_entries, bkt_task, + tmp_task, task_entry, hash) { + hash_del(&task_entry->hash); + kfree(task_entry); + } +} + +static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + memset(&task_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + } +} + +static void add_uid_tasks_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) +{ + struct task_entry *task_entry = find_or_register_task(uid_entry, task); + struct io_stats *task_io_slot = &task_entry->io[slot]; + + task_io_slot->read_bytes += task->ioac.read_bytes; + task_io_slot->write_bytes += compute_write_bytes(task); + task_io_slot->rchar += task->ioac.rchar; + task_io_slot->wchar += task->ioac.wchar; + task_io_slot->fsync += task->ioac.syscfs; +} + +static void compute_io_uid_tasks(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + compute_io_bucket_stats(&task_entry->io[uid_entry->state], + &task_entry->io[UID_STATE_TOTAL_CURR], + &task_entry->io[UID_STATE_TOTAL_LAST], + &task_entry->io[UID_STATE_DEAD_TASKS]); + } +} + +static void show_io_uid_tasks(struct seq_file *m, struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + /* Separated by comma because space exists in task comm */ + seq_printf(m, "task,%s,%lu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", + task_entry->comm, + (unsigned long)task_entry->pid, + task_entry->io[UID_STATE_FOREGROUND].rchar, + task_entry->io[UID_STATE_FOREGROUND].wchar, + task_entry->io[UID_STATE_FOREGROUND].read_bytes, + task_entry->io[UID_STATE_FOREGROUND].write_bytes, + task_entry->io[UID_STATE_BACKGROUND].rchar, + task_entry->io[UID_STATE_BACKGROUND].wchar, + task_entry->io[UID_STATE_BACKGROUND].read_bytes, + task_entry->io[UID_STATE_BACKGROUND].write_bytes, + task_entry->io[UID_STATE_FOREGROUND].fsync, + task_entry->io[UID_STATE_BACKGROUND].fsync); + } +} +#else +static void remove_uid_tasks(struct uid_entry *uid_entry) {}; +static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {}; +static void compute_io_uid_tasks(struct uid_entry *uid_entry) {}; +static void show_io_uid_tasks(struct seq_file *m, + struct uid_entry *uid_entry) {} +#endif + static struct uid_entry *find_uid_entry(uid_t uid) { struct uid_entry *uid_entry; @@ -88,7 +320,9 @@ static struct uid_entry *find_or_register_uid(uid_t uid) return NULL; uid_entry->uid = uid; - +#ifdef CONFIG_UID_SYS_STATS_DEBUG + hash_init(uid_entry->task_entries); +#endif hash_add(hash_table, &uid_entry->hash, uid); return uid_entry; @@ -203,6 +437,7 @@ static ssize_t uid_remove_write(struct file *file, hash_for_each_possible_safe(hash_table, uid_entry, tmp, hash, (uid_t)uid_start) { if (uid_start == uid_entry->uid) { + remove_uid_tasks(uid_entry); hash_del(&uid_entry->hash); kfree(uid_entry); } @@ -219,14 +454,6 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; -static u64 compute_write_bytes(struct task_struct *task) -{ - if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) - return 0; - - return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; -} - static void add_uid_io_stats(struct uid_entry *uid_entry, struct task_struct *task, int slot) { @@ -239,44 +466,6 @@ static void add_uid_io_stats(struct uid_entry *uid_entry, io_slot->fsync += task->ioac.syscfs; } -static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, - struct io_stats *io_curr, - struct io_stats *io_last, - struct io_stats *io_dead) -{ - s64 delta; - - delta = io_curr->read_bytes + io_dead->read_bytes - - io_last->read_bytes; - if (delta > 0) - io_bucket->read_bytes += delta; - - delta = io_curr->write_bytes + io_dead->write_bytes - - io_last->write_bytes; - if (delta > 0) - io_bucket->write_bytes += delta; - - delta = io_curr->rchar + io_dead->rchar - io_last->rchar; - if (delta > 0) - io_bucket->rchar += delta; - - delta = io_curr->wchar + io_dead->wchar - io_last->wchar; - if (delta > 0) - io_bucket->wchar += delta; - - delta = io_curr->fsync + io_dead->fsync - io_last->fsync; - if (delta > 0) - io_bucket->fsync += delta; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; - - memset(io_dead, 0, sizeof(struct io_stats)); -} - static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; @@ -285,9 +474,11 @@ static void update_io_stats_all_locked(void) unsigned long bkt; uid_t uid; - hash_for_each(hash_table, bkt, uid_entry, hash) + hash_for_each(hash_table, bkt, uid_entry, hash) { memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); + set_io_uid_tasks_zero(uid_entry); + } rcu_read_lock(); do_each_thread(temp, task) { @@ -300,10 +491,11 @@ static void update_io_stats_all_locked(void) rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + compute_io_bucket_stats(&uid_entry->io[uid_entry->state], &uid_entry->io[UID_STATE_TOTAL_CURR], &uid_entry->io[UID_STATE_TOTAL_LAST], &uid_entry->io[UID_STATE_DEAD_TASKS]); + compute_io_uid_tasks(uid_entry); } } @@ -314,6 +506,7 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); + set_io_uid_tasks_zero(uid_entry); rcu_read_lock(); do_each_thread(temp, task) { @@ -323,12 +516,14 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry) } while_each_thread(temp, task); rcu_read_unlock(); - compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + compute_io_bucket_stats(&uid_entry->io[uid_entry->state], &uid_entry->io[UID_STATE_TOTAL_CURR], &uid_entry->io[UID_STATE_TOTAL_LAST], &uid_entry->io[UID_STATE_DEAD_TASKS]); + compute_io_uid_tasks(uid_entry); } + static int uid_io_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; @@ -340,21 +535,22 @@ static int uid_io_show(struct seq_file *m, void *v) hash_for_each(hash_table, bkt, uid_entry, hash) { seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", - uid_entry->uid, - uid_entry->io[UID_STATE_FOREGROUND].rchar, - uid_entry->io[UID_STATE_FOREGROUND].wchar, - uid_entry->io[UID_STATE_FOREGROUND].read_bytes, - uid_entry->io[UID_STATE_FOREGROUND].write_bytes, - uid_entry->io[UID_STATE_BACKGROUND].rchar, - uid_entry->io[UID_STATE_BACKGROUND].wchar, - uid_entry->io[UID_STATE_BACKGROUND].read_bytes, - uid_entry->io[UID_STATE_BACKGROUND].write_bytes, - uid_entry->io[UID_STATE_FOREGROUND].fsync, - uid_entry->io[UID_STATE_BACKGROUND].fsync); + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); + + show_io_uid_tasks(m, uid_entry); } rt_mutex_unlock(&uid_lock); - return 0; } From 082978340f4102bd1a234d38db833f69612d8094 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 31 Jul 2017 15:45:21 -0700 Subject: [PATCH 238/296] ANDROID: Fix missing header file for get_cmdline() call commit ab6f11cf39b6a605b0e644ac1fde21d517d56d40 uid_sys_stats: log task io with a debug flag causes on some builds next error: error: implicit declaration of function 'get_cmdline' [-Werror=implicit-function-declaration] Change-Id: I41adaf5af999b94bac41e3f17a67cfc88400ee31 Signed-off-by: Dmitry Shmidt Signed-off-by: Harsh Shandilya Signed-off-by: anupritaisno1 --- drivers/misc/uid_sys_stats.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 9e162ab1108d5..f622fd7400acf 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include From 9881b400934064873fafd0873a9057b7a273709a Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sun, 19 Mar 2017 15:15:54 -0700 Subject: [PATCH 239/296] mdss: livedisplay: Fix memory leaks in mdss_livedisplay_update() parse_dsi_cmds() allocates two blocks of memory and stores their addresses in a stack-allocated variable, but they are never freed, so when the function exits, all references to them are gone. Free the allocated memory after using it in order to fix the memory leaks. Change-Id: Ie574848e2429167fc38ed39975feb3df68ed2aed Signed-off-by: Sultanxda # Conflicts: # drivers/video/msm/mdss/mdss_livedisplay.c Signed-off-by: anupritaisno1 --- drivers/video/msm/mdss/mdss_livedisplay.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/msm/mdss/mdss_livedisplay.c b/drivers/video/msm/mdss/mdss_livedisplay.c index d34f5ced902d5..9537471115e27 100644 --- a/drivers/video/msm/mdss/mdss_livedisplay.c +++ b/drivers/video/msm/mdss/mdss_livedisplay.c @@ -270,6 +270,8 @@ static void mdss_livedisplay_worker(struct work_struct *work) ret = parse_dsi_cmds(&dsi_cmds, mlc->cmd_buf, len); if (ret == 0) { mdss_dsi_panel_cmds_send(ctrl_pdata, &dsi_cmds, CMD_REQ_COMMIT); + kfree(dsi_cmds.buf); + kfree(dsi_cmds.cmds); } else { pr_err("%s: error parsing DSI command! ret=%d", __func__, ret); } From 6ee82b97c166925ee38a8cc1b02b5e544929c814 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Mon, 23 May 2016 15:53:51 -0700 Subject: [PATCH 240/296] mmc: clk_scaling: Fix scale_down_in_low_wr_load sysfs return value Only return -EINVAL on failure, not for all cases. Signed-off-by: anupritaisno1 --- drivers/mmc/core/host.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 1ec2910c1df4f..552c7d3401d3b 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -709,18 +709,16 @@ static ssize_t store_scale_down_in_low_wr_load(struct device *dev, { struct mmc_host *host = cls_dev_to_mmc_host(dev); unsigned long value; - int retval = -EINVAL; if (!host) - goto out; + return -EINVAL; if (!host->card || kstrtoul(buf, 0, &value)) - goto out; + return -EINVAL; host->clk_scaling.scale_down_in_low_wr_load = value; -out: - return retval; + return count; } static ssize_t show_up_threshold(struct device *dev, From f8cdea11dc943e78dce63bb8341795ec75c044f4 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Tue, 2 May 2017 13:40:29 -0700 Subject: [PATCH 241/296] input: synaptics: s3320: Fix unsafe memory accesses in procfs nodes *Use snprintf() instead of sprintf() to prevent buffer overflows *Do not directly access user memory in type##_write_func() *Simplify type##_read_func() routines Signed-off-by: Sultanxda # Conflicts: # drivers/input/touchscreen/synaptics_driver_s3320.c # Conflicts: # drivers/input/touchscreen/synaptics_driver_s3320.c Signed-off-by: anupritaisno1 --- drivers/input/touchscreen/synaptics_driver_s3320.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/synaptics_driver_s3320.c b/drivers/input/touchscreen/synaptics_driver_s3320.c index 496058d85276e..b45b4a03b5b17 100644 --- a/drivers/input/touchscreen/synaptics_driver_s3320.c +++ b/drivers/input/touchscreen/synaptics_driver_s3320.c @@ -1342,7 +1342,7 @@ static ssize_t i2c_device_test_read_func(struct file *file, char __user *user_bu if(!ts_g) return ret; TPD_DEBUG("gesture enable is: %d\n", ts->double_enable); - ret = sprintf(page, "%d\n", ts->i2c_device_test); + ret = snprintf(page, sizeof(page), "%d\n", ts->i2c_device_test); ret = simple_read_from_buffer(user_buf, count, ppos, page, strlen(page)); return ret; } @@ -1378,7 +1378,7 @@ static ssize_t type##_read_func(struct file *file, char __user *user_buf, size_t { \ int ret = 0; \ char page[PAGESIZE]; \ - ret = sprintf(page, "%d\n", gesture_enabled(type)); \ + ret = snprintf(page, sizeof(page), "%d\n", gesture_enabled(type)); \ ret = simple_read_from_buffer(user_buf, count, ppos, page, strlen(page)); \ return ret; \ } \ @@ -1477,7 +1477,7 @@ static ssize_t tp_glove_read_func(struct file *file, char __user *user_buf, size if(!ts) return ret; printk("glove mode enable is: %d\n", ts->glove_enable); - ret = sprintf(page, "%d\n", ts->glove_enable); + ret = snprintf(page, sizeof(page), "%d\n", ts->glove_enable); ret = simple_read_from_buffer(user_buf, count, ppos, page, strlen(page)); return ret; } @@ -1524,7 +1524,7 @@ static ssize_t tp_sleep_read_func(struct file *file, char __user *user_buf, size int ret = 0; char page[PAGESIZE]; printk("sleep mode enable is: %d\n", sleep_enable); - ret = sprintf(page, "%d\n", sleep_enable); + ret = snprintf(page, sizeof(page), "%d\n", sleep_enable); ret = simple_read_from_buffer(user_buf, count, ppos, page, strlen(page)); return ret; } @@ -1598,7 +1598,7 @@ static ssize_t vendor_id_read_func(struct file *file, char __user *user_buf, siz { int ret = 0; char page[4]; - ret = sprintf(page, "6\n"); + ret = snprintf(page, sizeof(page), "6\n"); ret = simple_read_from_buffer(user_buf, count, ppos, page, strlen(page)); return ret; } @@ -2418,7 +2418,7 @@ static ssize_t changer_read_func(struct file *file, char __user *user_buf, size_ struct synaptics_ts_data *ts = ts_g; if(!ts) return ret; - ret = sprintf(page, "the changer is %s!\n", ts->changer_connet?("conneted"):("disconneted")); + ret = snprintf(page, sizeof(page), "the changer is %s!\n", ts->changer_connet?("conneted"):("disconneted")); ret = simple_read_from_buffer(user_buf, count, ppos, page, strlen(page)); return ret; } From e5712ba8cda29e03e87d64e26572c960278da15b Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Tue, 2 May 2017 14:43:06 -0700 Subject: [PATCH 242/296] input: synaptics: s1302: Don't directly access user memory in fw_write This fixes the following KASan error: ================================================================== BUG: KASAN: user-memory-access on address 0000007fd916e3b1 Read of size 1 by task init/1 CPU: 1 PID: 1 Comm: init Not tainted 3.18.31-Sultan #47 Hardware name: Qualcomm Technologies, Inc. MSM 8996 v3.0 + PMI8996 MTP (DT) Call trace: [] dump_backtrace+0x0/0x298 [] show_stack+0x14/0x1c [] dump_stack+0x98/0xc0 [] kasan_report+0x120/0x4e8 [] __asan_load1+0x24/0x50 [] vsscanf+0x2c8/0x9d8 [] sscanf+0x6c/0x8c [] synaptics_s1302_fw_write+0x74/0x324 [] proc_reg_write+0xc0/0xec [] vfs_write+0x104/0x218 [] SyS_write+0x68/0xc8 ================================================================== Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- drivers/input/touchscreen/synaptics_driver_s1302.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/synaptics_driver_s1302.c b/drivers/input/touchscreen/synaptics_driver_s1302.c index 7eb354a028516..afc8a4ef23804 100644 --- a/drivers/input/touchscreen/synaptics_driver_s1302.c +++ b/drivers/input/touchscreen/synaptics_driver_s1302.c @@ -863,7 +863,8 @@ static int synaptics_s1302_fw_show(struct seq_file *seq, void *offset) } static ssize_t synaptics_s1302_fw_write(struct file *file, const char __user *page, size_t t, loff_t *lo) { - int val = 0; + char val; + int ret; if (NULL == tc_g) return -EINVAL; @@ -871,8 +872,11 @@ static ssize_t synaptics_s1302_fw_write(struct file *file, const char __user *pa if (t > 2) return -EINVAL; - sscanf(page, "%d", &val); + ret = copy_from_user(&val, page, sizeof(val)); + if (ret) + return ret; + val -= '0'; if(!val) val = force_update; From 0c40fadc76aa5cf15a2ad98c6edcb214b91dd88a Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Tue, 25 Apr 2017 14:23:42 -0700 Subject: [PATCH 243/296] power: msm-core: Compile out temperature polling The temperature polling used here is expensive; according to top, it regularly eats up 10% of the CPU. Frequently spending 10% of CPU time on an *energy efficiency* feature is nonsense, so we'll just use the default temperature for power calculations so that we can still make use of power-aware scheduling. Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- drivers/power/qcom/msm-core.c | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c index 1b98a090c36cd..a39fe6d92d47e 100644 --- a/drivers/power/qcom/msm-core.c +++ b/drivers/power/qcom/msm-core.c @@ -99,17 +99,23 @@ struct cpu_static_info { static DEFINE_MUTEX(policy_update_mutex); static DEFINE_MUTEX(kthread_update_mutex); static DEFINE_SPINLOCK(update_lock); +#ifdef ENABLE_TSENS_SAMPLING static struct delayed_work sampling_work; static struct completion sampling_completion; static struct task_struct *sampling_task; static int low_hyst_temp; static int high_hyst_temp; +#endif static struct platform_device *msm_core_pdev; static struct cpu_activity_info activity[NR_CPUS]; DEFINE_PER_CPU(struct cpu_pstate_pwr *, ptable); static struct cpu_pwr_stats cpu_stats[NR_CPUS]; ALLOCATE_2D_ARRAY(uint32_t); +/* + * Userspace checks for the presence of poll_ms and disabled, so keep them + * even when ENABLE_TSENS_SAMPLING isn't used. + */ static int poll_ms; module_param_named(polling_interval, poll_ms, int, S_IRUGO | S_IWUSR | S_IWGRP); @@ -117,6 +123,7 @@ module_param_named(polling_interval, poll_ms, int, static int disabled; module_param_named(disabled, disabled, int, S_IRUGO | S_IWUSR | S_IWGRP); +#ifdef ENABLE_TSENS_SAMPLING static bool in_suspend; static bool activate_power_table; @@ -190,6 +197,7 @@ static void core_temp_notify(enum thermal_trip_type type, complete(&sampling_completion); } +#endif #ifdef VENDOR_EDIT /* ic, provide pcost based on requested frequency and temperature */ @@ -250,6 +258,7 @@ static void repopulate_stats(int cpu) void trigger_cpu_pwr_stats_calc(void) { +#ifdef ENABLE_TSENS_SAMPLING int cpu; static long prev_temp[NR_CPUS]; struct cpu_activity_info *cpu_node; @@ -277,6 +286,7 @@ void trigger_cpu_pwr_stats_calc(void) repopulate_stats(cpu); } spin_unlock(&update_lock); +#endif } EXPORT_SYMBOL(trigger_cpu_pwr_stats_calc); @@ -322,6 +332,7 @@ static void update_related_freq_table(struct cpufreq_policy *policy) } } +#ifdef ENABLE_TSENS_SAMPLING static __ref int do_sampling(void *data) { int cpu; @@ -359,6 +370,7 @@ static __ref int do_sampling(void *data) } return 0; } +#endif static void clear_static_power(struct cpu_static_info *sp) { @@ -472,7 +484,9 @@ static int update_userspace_power(struct sched_params __user *argp) spin_unlock(&update_lock); mutex_unlock(&policy_update_mutex); +#ifdef ENABLE_TSENS_SAMPLING activate_power_table = true; +#endif return 0; failed: @@ -565,6 +579,7 @@ static int msm_core_release(struct inode *inode, struct file *file) return 0; } +#ifdef ENABLE_TSENS_SAMPLING static inline void init_sens_threshold(struct sensor_threshold *threshold, enum thermal_trip_type trip, long temp, void *data) @@ -574,6 +589,7 @@ static inline void init_sens_threshold(struct sensor_threshold *threshold, threshold->data = data; threshold->notify = (void *)core_temp_notify; } +#endif static int msm_core_stats_init(struct device *dev, int cpu) { @@ -601,6 +617,7 @@ static int msm_core_stats_init(struct device *dev, int cpu) return 0; } +#ifdef ENABLE_TSENS_SAMPLING static int msm_core_task_init(struct device *dev) { init_completion(&sampling_completion); @@ -612,6 +629,7 @@ static int msm_core_task_init(struct device *dev) } return 0; } +#endif struct cpu_pwr_stats *get_cpu_pwr_stats(void) { @@ -709,6 +727,7 @@ static int msm_core_dyn_pwr_init(struct platform_device *pdev, return ret; } +#ifdef ENABLE_TSENS_SAMPLING static int msm_core_tsens_init(struct device_node *node, int cpu) { int ret = 0; @@ -762,6 +781,7 @@ static int msm_core_tsens_init(struct device_node *node, int cpu) return ret; } +#endif static int msm_core_mpidr_init(struct device_node *node) { @@ -827,6 +847,7 @@ struct notifier_block cpu_policy = { .notifier_call = msm_core_cpu_policy_handler }; +#ifdef ENABLE_TSENS_SAMPLING static int system_suspend_handler(struct notifier_block *nb, unsigned long val, void *data) { @@ -874,6 +895,7 @@ static int system_suspend_handler(struct notifier_block *nb, return NOTIFY_OK; } +#endif static int msm_core_freq_init(void) { @@ -931,9 +953,13 @@ static int msm_core_params_init(struct platform_device *pdev) activity[cpu].mpidr = mpidr; +#ifdef ENABLE_TSENS_SAMPLING ret = msm_core_tsens_init(child_node, cpu); if (ret) return ret; +#else + activity[cpu].temp = DEFAULT_TEMP; +#endif if (!activity[cpu].sp->table) continue; @@ -1029,14 +1055,17 @@ static int uio_init(struct platform_device *pdev) static int msm_core_dev_probe(struct platform_device *pdev) { int ret = 0; +#ifdef ENABLE_TSENS_SAMPLING char *key = NULL; struct device_node *node; int cpu; +#endif if (!pdev) return -ENODEV; msm_core_pdev = pdev; +#ifdef ENABLE_TSENS_SAMPLING node = pdev->dev.of_node; if (!node) return -ENODEV; @@ -1055,6 +1084,7 @@ static int msm_core_dev_probe(struct platform_device *pdev) ret = of_property_read_u32(node, key, &poll_ms); if (ret) pr_info("msm-core initialized without polling period\n"); +#endif ret = uio_init(pdev); @@ -1075,6 +1105,7 @@ static int msm_core_dev_probe(struct platform_device *pdev) if (ret) goto failed; +#ifdef ENABLE_TSENS_SAMPLING ret = msm_core_task_init(&pdev->dev); if (ret) goto failed; @@ -1084,8 +1115,9 @@ static int msm_core_dev_probe(struct platform_device *pdev) INIT_DEFERRABLE_WORK(&sampling_work, samplequeue_handle); schedule_delayed_work(&sampling_work, msecs_to_jiffies(0)); - cpufreq_register_notifier(&cpu_policy, CPUFREQ_POLICY_NOTIFIER); pm_notifier(system_suspend_handler, 0); +#endif + cpufreq_register_notifier(&cpu_policy, CPUFREQ_POLICY_NOTIFIER); return 0; failed: free_dyn_memory(); @@ -1094,11 +1126,14 @@ static int msm_core_dev_probe(struct platform_device *pdev) static int msm_core_remove(struct platform_device *pdev) { +#ifdef ENABLE_TSENS_SAMPLING int cpu; +#endif struct uio_info *info = dev_get_drvdata(&pdev->dev); uio_unregister_device(info); +#ifdef ENABLE_TSENS_SAMPLING for_each_possible_cpu(cpu) { if (activity[cpu].sensor_id < 0) continue; @@ -1108,6 +1143,7 @@ static int msm_core_remove(struct platform_device *pdev) sensor_cancel_trip(activity[cpu].sensor_id, &activity[cpu].low_threshold); } +#endif free_dyn_memory(); misc_deregister(&msm_core_device); return 0; From 87dc924550103fb13aaad915db9b3a1bad8291b5 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Tue, 23 Aug 2016 21:47:05 +0100 Subject: [PATCH 244/296] ARM: dts: Enable wakeup events for the volume keys During offloaded music playback, the CPU can sleep, causing the volume keys to not respond when attempting to interact with the app playing the music (e.g. volume does not go up/down, skipping tracks using volume keys does not work). Wake the system upon receiving volume key input in order to fix this. Change-Id: I117c996aa589073d1b98681356892126c1e616fd # Conflicts: # arch/arm64/boot/dts/14049_HW/msm8994-mtp.dtsi Signed-off-by: anupritaisno1 --- arch/arm/boot/dts/qcom/msm8994-mtp.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8994-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8994-mtp.dtsi index 12288e95f1447..30c2559d24206 100755 --- a/arch/arm/boot/dts/qcom/msm8994-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8994-mtp.dtsi @@ -524,6 +524,7 @@ gpios = <&pm8994_gpios 3 0x1>; linux,input-type = <1>; linux,code = <115>; + gpio-key,wakeup; debounce-interval = <15>; }; @@ -534,6 +535,7 @@ gpios = <&pm8994_gpios 2 0x1>; linux,input-type = <1>; linux,code = <114>; + gpio-key,wakeup; debounce-interval = <15>; }; //endif VENDOR_EDIT From 36170d97099460357fcbb3554c19ab7f3b39a6de Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sun, 2 Jul 2017 14:27:01 -0700 Subject: [PATCH 245/296] msm: vidc: Fix broken debugfs creation error checks and error paths The debugfs helper functions do not always return NULL when they fail; instead, they can return an error number casted as a pointer, so that their users have the option to determine the exact cause of failure. Use the IS_ERR_OR_NULL() helper when checking for debugfs errors to fix the error checks. Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- .../media/platform/msm/vidc/msm_vidc_debug.c | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c index d92afcb1684c3..73b00ca778190 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -253,7 +253,7 @@ struct dentry *msm_vidc_debugfs_init_drv(void) struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, struct dentry *parent) { - struct dentry *dir = NULL; + struct dentry *dir; char debugfs_name[MAX_DEBUGFS_NAME]; if (!core) { dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core); @@ -262,22 +262,24 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id); dir = debugfs_create_dir(debugfs_name, parent); - if (!dir) { + if (IS_ERR_OR_NULL(dir)) { dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); goto failed_create_dir; } - if (!debugfs_create_file("info", S_IRUGO, dir, core, &core_info_fops)) { + if (IS_ERR_OR_NULL(debugfs_create_file("info", S_IRUGO, dir, core, &core_info_fops))) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); - goto failed_create_dir; + goto failed_create_file; } - if (!debugfs_create_file("trigger_ssr", S_IWUSR, - dir, core, &ssr_fops)) { + if (IS_ERR_OR_NULL(debugfs_create_file("trigger_ssr", S_IWUSR, + dir, core, &ssr_fops))) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); - goto failed_create_dir; + goto failed_create_file; } +failed_create_file: + debugfs_remove_recursive(dir); failed_create_dir: - return dir; + return NULL; } static int inst_info_open(struct inode *inode, struct file *file) @@ -452,7 +454,7 @@ static const struct file_operations inst_info_fops = { struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, struct dentry *parent) { - struct dentry *dir = NULL, *info = NULL; + struct dentry *dir, *info; char debugfs_name[MAX_DEBUGFS_NAME]; struct core_inst_pair *idata = NULL; @@ -473,14 +475,14 @@ struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, idata->inst = inst; dir = debugfs_create_dir(debugfs_name, parent); - if (!dir) { + if (IS_ERR_OR_NULL(dir)) { dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); goto failed_create_dir; } info = debugfs_create_file("info", S_IRUGO, dir, idata, &inst_info_fops); - if (!info) { + if (IS_ERR_OR_NULL(info)) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); goto failed_create_file; } @@ -491,11 +493,10 @@ struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, failed_create_file: debugfs_remove_recursive(dir); - dir = NULL; failed_create_dir: kfree(idata); exit: - return dir; + return NULL; } void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst) From 3888ca350774070e4660814b7341574b9c888a8c Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sun, 4 Feb 2018 02:26:33 -0800 Subject: [PATCH 246/296] net: sch_generic: Remove unnecessary watchdog warning We're never going to debug these ugly warnings. Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- net/sched/sch_generic.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a7f838b45dc85..0c6d44898e4b2 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -250,11 +250,8 @@ static void dev_watchdog(unsigned long arg) } } - if (some_queue_timedout) { - WARN_ONCE(1, KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit queue %u timed out\n", - dev->name, netdev_drivername(dev), i); + if (some_queue_timedout) dev->netdev_ops->ndo_tx_timeout(dev); - } if (!mod_timer(&dev->watchdog_timer, round_jiffies(jiffies + dev->watchdog_timeo))) From 4cf7fd38ae90656fb147069534fff74e835126ff Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sun, 18 Feb 2018 11:28:37 -0800 Subject: [PATCH 247/296] i2c-msm-v2: Ensure system is always active for i2c transfers There are quite a few i2c devices that are intended to remain active while the system is suspended, which means that when one of them fires off an interrupt, the subsequent i2c transfers inside the interrupt handler will fail. To fix this, make the entire system resume when an i2c transfer is requested while the system is suspended. i2c transfer requests will be blocked until the system is completely resumed, so that they won't fail. Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- drivers/i2c/busses/i2c-msm-v2.c | 63 ++++++++++++++++++++++++++------- include/linux/i2c/i2c-msm-v2.h | 1 + 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index 90c078a471262..f0c696ca02d01 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef DEBUG static const enum msm_i2_debug_level DEFAULT_DBG_LVL = MSM_DBG; @@ -44,6 +45,9 @@ static const enum msm_i2_debug_level DEFAULT_DBG_LVL = MSM_DBG; static const enum msm_i2_debug_level DEFAULT_DBG_LVL = MSM_ERR; #endif +static DECLARE_COMPLETION(i2c_resume_done); +static atomic_t i2c_suspended = ATOMIC_INIT(0); + /* Forward declarations */ static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl); static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, @@ -2197,19 +2201,8 @@ static int i2c_msm_pm_clk_prepare_enable(struct i2c_msm_ctrl *ctrl) static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl) { int ret; - struct i2c_msm_xfer *xfer = &ctrl->xfer; mutex_lock(&ctrl->xfer.mtx); - /* if system is suspended just bail out */ - if (ctrl->pwr_state == I2C_MSM_PM_SYS_SUSPENDED) { - struct i2c_msg *msgs = xfer->msgs + xfer->cur_buf.msg_idx; - dev_err(ctrl->dev, - "slave:0x%x is calling xfer when system is suspended\n", - msgs->addr); - mutex_unlock(&ctrl->xfer.mtx); - return -EIO; - } - pm_runtime_get_sync(ctrl->dev); /* * if runtime PM callback was not invoked (when both runtime-pm @@ -2287,10 +2280,26 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) int ret = 0; struct i2c_msm_ctrl *ctrl = i2c_get_adapdata(adap); struct i2c_msm_xfer *xfer = &ctrl->xfer; + bool release_wakeup = false; + + /* If system is suspended then make it resume */ + if (atomic_read(&i2c_suspended)) { + release_wakeup = true; + pm_stay_awake(ctrl->dev); + ret = wait_for_completion_timeout(&i2c_resume_done, + msecs_to_jiffies(I2C_MSM_TIMEOUT_PM_RESUME_MSEC)); + if (!ret) { + ret = -EIO; + dev_err(ctrl->dev, + "slave:0x%x is calling xfer when system is suspended\n", + msgs->addr); + goto end; + } + } ret = i2c_msm_pm_xfer_start(ctrl); if (ret) - return ret; + goto end; /* init xfer */ xfer->msgs = msgs; @@ -2347,6 +2356,9 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_msm_prof_evnt_dump(ctrl); i2c_msm_pm_xfer_end(ctrl); +end: + if (release_wakeup) + pm_relax(ctrl->dev); return ret; } @@ -2749,6 +2761,31 @@ static int i2c_msm_pm_sys_resume_noirq(struct device *dev) mutex_unlock(&ctrl->xfer.mtx); return 0; } + +static int pm_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + /* Wait until the entire system is resumed to unblock i2c transfers */ + switch (action) { + case PM_POST_SUSPEND: + atomic_set(&i2c_suspended, 0); + complete_all(&i2c_resume_done); + break; + case PM_SUSPEND_PREPARE: + reinit_completion(&i2c_resume_done); + atomic_set(&i2c_suspended, 1); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block pm_notifier_callback_nb = { + .notifier_call = pm_notifier_callback, + .priority = INT_MIN, +}; #endif #ifdef CONFIG_PM_RUNTIME @@ -2820,6 +2857,7 @@ static int i2c_msm_frmwrk_reg(struct platform_device *pdev, ctrl->adapter.nr = pdev->id; ctrl->adapter.dev.parent = &pdev->dev; + device_init_wakeup(ctrl->dev, true); ret = i2c_add_numbered_adapter(&ctrl->adapter); if (ret) { dev_err(ctrl->dev, "error i2c_add_adapter failed\n"); @@ -2971,6 +3009,7 @@ static struct platform_driver i2c_msm_driver = { static int i2c_msm_init(void) { + register_pm_notifier(&pm_notifier_callback_nb); return platform_driver_register(&i2c_msm_driver); } subsys_initcall(i2c_msm_init); diff --git a/include/linux/i2c/i2c-msm-v2.h b/include/linux/i2c/i2c-msm-v2.h index 02cae9b8759ef..f140b9f6a689e 100644 --- a/include/linux/i2c/i2c-msm-v2.h +++ b/include/linux/i2c/i2c-msm-v2.h @@ -201,6 +201,7 @@ enum i2c_msm_power_state { #define I2C_MSM_MAX_POLL_MSEC (100) #define I2C_MSM_TIMEOUT_SAFTY_COEF (10) #define I2C_MSM_TIMEOUT_MIN_USEC (500000) +#define I2C_MSM_TIMEOUT_PM_RESUME_MSEC (5000) #define I2C_QUP_MAX_BUS_RECOVERY_RETRY (10) /* QUP v2 tags */ From 2f52e65b579188a966a45e0497f145e792554c06 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Thu, 20 Oct 2016 16:35:30 -0700 Subject: [PATCH 248/296] misc: fpc1020: Wait for uevent to send before exiting IRQ handler Force the IRQ handler to wait until userspace is updated with the new IRQ status so that interrupts are always processed synchronously. Signed-off-by: Sultanxda # Conflicts: # drivers/input/misc/fpc1020_tee.c Signed-off-by: anupritaisno1 --- drivers/input/misc/fpc1020_tee.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/fpc1020_tee.c b/drivers/input/misc/fpc1020_tee.c index e5a464687982d..2bebf34337d3f 100755 --- a/drivers/input/misc/fpc1020_tee.c +++ b/drivers/input/misc/fpc1020_tee.c @@ -134,6 +134,7 @@ struct fpc1020_data { struct notifier_block fb_notif; #endif struct work_struct pm_work; + struct completion irq_sent; }; static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020, @@ -268,8 +269,11 @@ static ssize_t irq_get(struct device* device, char* buffer) { struct fpc1020_data* fpc1020 = dev_get_drvdata(device); + ssize_t count; int irq = gpio_get_value(fpc1020->irq_gpio); - return scnprintf(buffer, PAGE_SIZE, "%i\n", irq); + count = scnprintf(buffer, PAGE_SIZE, "%i\n", irq); + complete(&fpc1020->irq_sent); + return count; } @@ -969,6 +973,9 @@ static irqreturn_t fpc1020_irq_handler(int irq, void *handle) sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name); + reinit_completion(&fpc1020->irq_sent); + wait_for_completion_timeout(&fpc1020->irq_sent, msecs_to_jiffies(100)); + if (fpc1020->screen_state) return IRQ_HANDLED; @@ -1136,6 +1143,7 @@ static int fpc1020_probe(struct spi_device *spi) irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; mutex_init(&fpc1020->lock); + init_completion(&fpc1020->irq_sent); rc = devm_request_threaded_irq(dev, gpio_to_irq(fpc1020->irq_gpio), NULL, fpc1020_irq_handler, irqf, dev_name(dev), fpc1020); From a6d846110e2e201b10857ad7fa6a3e5651c8ea76 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Mon, 24 Apr 2017 02:54:22 -0700 Subject: [PATCH 249/296] PM / devfreq: Add a governor white list Many of the devfreq governors compiled in are unstable/incomplete, so we don't want users switching to them. Compiling out the unused governors is messy because many other drivers link to them, and there are not proper Add a white list of the only governors intended for use to the governor sysfs node in order to prevent users from enabling governors that unstable. Adapted by aviraxp for OP2. Signed-off-by: anupritaisno1 --- drivers/devfreq/devfreq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index d366104d3fc85..cddfefb334bb3 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -755,7 +755,8 @@ static ssize_t store_governor(struct device *dev, struct device_attribute *attr, strncmp(str_governor, "performance", DEVFREQ_NAME_LEN) && strncmp(str_governor, "powersave", DEVFREQ_NAME_LEN) && strncmp(str_governor, "mem_latency", DEVFREQ_NAME_LEN) && - strncmp(str_governor, "msm-adreno-tz", DEVFREQ_NAME_LEN)) + strncmp(str_governor, "msm-adreno-tz", DEVFREQ_NAME_LEN) && + strncmp(str_governor, "simple_ondemand", DEVFREQ_NAME_LEN)) return -EINVAL; mutex_lock(&devfreq_list_lock); From 3583ee324656441ff7a19402fd1fccd12d138320 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Tue, 21 Mar 2017 15:29:57 -0700 Subject: [PATCH 250/296] msm: camera: sensor: Fix memory leaks when sensor probe fails The power settings for a camera sensor can be parsed and have its memory never freed in the error path if part of the probe sequence fails after the power settings are parsed. Free the power settings in the error paths to fix the memory leaks. Signed-off-by: Sultanxda Signed-off-by: Francisco Franco Signed-off-by: anupritaisno1 --- .../platform/msm/camera_v2/sensor/msm_sensor_driver.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 9b7119b4e7df5..805beae722aec 100755 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -591,6 +591,7 @@ static int32_t msm_sensor_get_power_settings(void *setting, power_info); if (rc < 0) { pr_err("failed"); + kfree(power_info->power_setting); return -EINVAL; } return rc; @@ -812,9 +813,8 @@ int32_t msm_sensor_driver_probe(void *setting, camera_info = kzalloc(sizeof(struct msm_camera_slave_info), GFP_KERNEL); if (!camera_info) { - pr_err("failed: no memory slave_info %p", camera_info); - goto free_slave_info; - + pr_err("failed: no memory slave_info %pK", camera_info); + goto free_power_settings; } s_ctrl->sensordata->slave_info = camera_info; @@ -977,6 +977,9 @@ int32_t msm_sensor_driver_probe(void *setting, s_ctrl->func_tbl->sensor_power_down(s_ctrl); free_camera_info: kfree(camera_info); +free_power_settings: + kfree(s_ctrl->sensordata->power_info.power_setting); + kfree(s_ctrl->sensordata->power_info.power_down_setting); free_slave_info: kfree(slave_info); return rc; From 45508e82f436b70560784bcc78b900ce89d6e99f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 14 Nov 2013 14:32:01 -0800 Subject: [PATCH 251/296] sched: replace INIT_COMPLETION with reinit_completion For the casual device driver writer, it is hard to remember when to use init_completion (to init a completion structure) or INIT_COMPLETION (to *reinit* a completion structure). Furthermore, while all other completion functions exepct a pointer as a parameter, INIT_COMPLETION does not. To make it easier to remember which function to use and to make code more readable, introduce a new inline function with the proper name and consistent argument type. Update the kernel-doc for init_completion while we are here. Signed-off-by: Wolfram Sang Acked-by: Linus Walleij (personally at LCE13) Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: anupritaisno1 --- include/linux/completion.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/include/linux/completion.h b/include/linux/completion.h index 33f0280fd5335..c2e813c57a707 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -19,8 +19,8 @@ * * See also: complete(), wait_for_completion() (and friends _timeout, * _interruptible, _interruptible_timeout, and _killable), init_completion(), - * and macros DECLARE_COMPLETION(), DECLARE_COMPLETION_ONSTACK(), and - * INIT_COMPLETION(). + * reinit_completion(), and macros DECLARE_COMPLETION(), + * DECLARE_COMPLETION_ONSTACK(). */ struct completion { unsigned int done; @@ -65,7 +65,7 @@ struct completion { /** * init_completion - Initialize a dynamically allocated completion - * @x: completion structure that is to be initialized + * @x: pointer to completion structure that is to be initialized * * This inline function will initialize a dynamically created completion * structure. @@ -76,6 +76,18 @@ static inline void init_completion(struct completion *x) init_waitqueue_head(&x->wait); } +/** + * reinit_completion - reinitialize a completion structure + * @x: pointer to completion structure that is to be reinitialized + * + * This inline function should be used to reinitialize a completion structure so it can + * be reused. This is especially important after complete_all() is used. + */ +static inline void reinit_completion(struct completion *x) +{ + x->done = 0; +} + extern void wait_for_completion(struct completion *); extern void wait_for_completion_io(struct completion *); extern int wait_for_completion_interruptible(struct completion *x); From 8a9cf2441f2623ac2e9e3b4fc54a68692c8d1cc3 Mon Sep 17 00:00:00 2001 From: Paul Keith Date: Sun, 1 Apr 2018 22:06:25 +0200 Subject: [PATCH 252/296] mdss: livedisplay: Don't allow non-contiguous presets * We currently allow having preset numbers that aren't continuous, but the rest of the preset code isn't designed for this, so disallow this * This also speeds up parse_dt by breaking out of the loop early if we don't have any presets instead of looking for all 10 presets even if the first isn't there Change-Id: I3a48b8ef027afcdbd27ba92bc43f5438c0f249f2 --- drivers/video/msm/mdss/mdss_livedisplay.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/msm/mdss/mdss_livedisplay.c b/drivers/video/msm/mdss/mdss_livedisplay.c index 9537471115e27..6c256bb69248c 100644 --- a/drivers/video/msm/mdss/mdss_livedisplay.c +++ b/drivers/video/msm/mdss/mdss_livedisplay.c @@ -582,6 +582,8 @@ int mdss_livedisplay_parse_dt(struct device_node *np, struct mdss_panel_info *pi &mlc->presets_len[mlc->num_presets]); if (mlc->presets_len[mlc->num_presets] > 0) mlc->num_presets++; + else + break; } if (mlc->num_presets) From 19955c1a8477bb327e69e65db8c4bee95cc3336a Mon Sep 17 00:00:00 2001 From: Jason Hrycay Date: Wed, 16 Nov 2016 10:08:57 -0600 Subject: [PATCH 253/296] cpufreq_stats: Fix stats leak during update policy When the cpufreq policy is moved from one CPU to another, the percpu stats_table is overwritten and leaked. Properly free the old stats table and ensure its protected in the non-sysfs paths of update policy and acct_update_power. The sysfs entries are removed in the cpufreq core driver before migrating the policy. We introduce a new spinlock specifically for these operations to avoid needed to convert all the other spinlocks into the irq safe variants since acct_update_power is typically called in ISR context. Change-Id: I95ff24c07834065cd0fd3c763a488a9843097a1d Signed-off-by: Jason Hrycay Reviewed-on: https://gerrit.mot.com/921752 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Reviewed-by: Igor Kovalenko --- drivers/cpufreq/cpufreq_stats.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index c266792711ae0..a268e6f6a5c50 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -20,6 +20,7 @@ #include static spinlock_t cpufreq_stats_lock; +static DEFINE_SPINLOCK(cpufreq_stats_table_lock); struct cpufreq_stats { unsigned int cpu; @@ -131,18 +132,24 @@ void acct_update_power(struct task_struct *task, cputime_t cputime) { struct cpufreq_power_stats *powerstats; struct cpufreq_stats *stats; unsigned int cpu_num, curr; + unsigned long flags; if (!task) return; cpu_num = task_cpu(task); powerstats = per_cpu(cpufreq_power_stats, cpu_num); + + spin_lock_irqsave(&cpufreq_stats_table_lock, flags); stats = per_cpu(cpufreq_stats_table, cpu_num); - if (!powerstats || !stats) + if (!powerstats || !stats) { + spin_unlock_irqrestore(&cpufreq_stats_table_lock, flags); return; + } curr = powerstats->curr[stats->last_index]; if (task->cpu_power != ULLONG_MAX) task->cpu_power += curr * cputime_to_usecs(cputime); + spin_unlock_irqrestore(&cpufreq_stats_table_lock, flags); } EXPORT_SYMBOL_GPL(acct_update_power); @@ -420,8 +427,18 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) { - struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, - policy->last_cpu); + struct cpufreq_stats *old; + struct cpufreq_stats *stat; + unsigned long flags; + + spin_lock_irqsave(&cpufreq_stats_table_lock, flags); + old = per_cpu(cpufreq_stats_table, policy->cpu); + stat = per_cpu(cpufreq_stats_table, policy->last_cpu); + + if (old) { + kfree(old->time_in_state); + kfree(old); + } if (!stat) return; @@ -431,6 +448,7 @@ static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) policy->last_cpu); per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL; stat->cpu = policy->cpu; + spin_unlock_irqrestore(&cpufreq_stats_table_lock, flags); } static void cpufreq_powerstats_create(unsigned int cpu, From 91c77fa909c5bdfbe07743214dc2f53b2aed897c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jan 2016 16:24:15 +0100 Subject: [PATCH 254/296] cfq-iosched: Don't group_idle if cfqq has big thinktime There is no point in idling on a cfq group if the only cfq queue that is there has too big thinktime. Signed-off-by: Jan Kara Acked-by: Tejun Heo Signed-off-by: Jens Axboe --- block/cfq-iosched.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index f17141946acd6..964b2132b9691 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -2705,6 +2705,7 @@ static bool cfq_should_idle(struct cfq_data *cfqd, struct cfq_queue *cfqq) static void cfq_arm_slice_timer(struct cfq_data *cfqd) { struct cfq_queue *cfqq = cfqd->active_queue; + struct cfq_rb_root *st = cfqq->service_tree; struct cfq_io_cq *cic; unsigned long sl, group_idle = 0; @@ -2755,8 +2756,13 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd) return; } - /* There are other queues in the group, don't do group idle */ - if (group_idle && cfqq->cfqg->nr_cfqq > 1) + /* + * There are other queues in the group or this is the only group and + * it has too big thinktime, don't do group idle. + */ + if (group_idle && + (cfqq->cfqg->nr_cfqq > 1 || + cfq_io_thinktime_big(cfqd, &st->ttime, true))) return; cfq_mark_cfqq_wait_request(cfqq); From 3a9ea2313995960c7abe0faa85956d4095efc16f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jan 2016 16:24:16 +0100 Subject: [PATCH 255/296] cfq-iosched: Reorder checks in cfq_should_preempt() Move check for preemption by rt class up. There is no functional change but it makes arguing about conditions simpler since we can be sure both cfq queues are from the same ioprio class. Acked-by: Tejun Heo Signed-off-by: Jan Kara Signed-off-by: Jens Axboe Signed-off-by: anupritaisno1 --- block/cfq-iosched.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 964b2132b9691..dde979a57cb82 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3816,6 +3816,13 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, if (cfq_slice_used(cfqq)) return true; + /* + * Allow an RT request to pre-empt an ongoing non-RT cfqq timeslice. + */ + if (cfq_class_rt(new_cfqq) && !cfq_class_rt(cfqq)) + return true; + + WARN_ON_ONCE(cfqq->ioprio_class != new_cfqq->ioprio_class); /* Allow preemption only if we are idling on sync-noidle tree */ if (cfqd->serving_wl_type == SYNC_NOIDLE_WORKLOAD && cfqq_type(new_cfqq) == SYNC_NOIDLE_WORKLOAD && @@ -3830,12 +3837,6 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, if ((rq->cmd_flags & REQ_PRIO) && !cfqq->prio_pending) return true; - /* - * Allow an RT request to pre-empt an ongoing non-RT cfqq timeslice. - */ - if (cfq_class_rt(new_cfqq) && !cfq_class_rt(cfqq)) - return true; - /* An idle queue should not be idle now for some reason */ if (RB_EMPTY_ROOT(&cfqq->sort_list) && !cfq_should_idle(cfqd, cfqq)) return true; From 48b820165345619a93e1b07a2362c6fb6ec87250 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jan 2016 16:24:17 +0100 Subject: [PATCH 256/296] cfq-iosched: Allow sync noidle workloads to preempt each other The original idea with preemption of sync noidle queues (introduced in commit 718eee0579b8 "cfq-iosched: fairness for sync no-idle queues") was that we service all sync noidle queues together, we don't idle on any of the queues individually and we idle only if there is no sync noidle queue to be served. This intention also matches the original test: if (cfqd->serving_type == SYNC_NOIDLE_WORKLOAD && new_cfqq->service_tree == cfqq->service_tree) return true; However since at that time cfqq->service_tree was not set for idling queues, this test was unreliable and was replaced in commit e4a229196a7c "cfq-iosched: fix no-idle preemption logic" by: if (cfqd->serving_type == SYNC_NOIDLE_WORKLOAD && cfqq_type(new_cfqq) == SYNC_NOIDLE_WORKLOAD && new_cfqq->service_tree->count == 1) return true; That was a reliable test but was actually doing something different - now we preempt sync noidle queue only if the new queue is the only one busy in the service tree. These days cfq queue is kept in service tree even if it is idling and thus the original check would be safe again. But since we actually check that cfq queues are in the same cgroup, of the same priority class and workload type (sync noidle), we know that new_cfqq is fine to preempt cfqq. So just remove the service tree check. Acked-by: Tejun Heo Signed-off-by: Jan Kara Signed-off-by: Jens Axboe Signed-off-by: anupritaisno1 --- block/cfq-iosched.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index dde979a57cb82..9c8406103783b 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3826,7 +3826,6 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, /* Allow preemption only if we are idling on sync-noidle tree */ if (cfqd->serving_wl_type == SYNC_NOIDLE_WORKLOAD && cfqq_type(new_cfqq) == SYNC_NOIDLE_WORKLOAD && - new_cfqq->service_tree->count == 2 && RB_EMPTY_ROOT(&cfqq->sort_list)) return true; From e37f2d2b141459caf1b9cc66f19cfa6553e85e3b Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jan 2016 16:24:19 +0100 Subject: [PATCH 257/296] cfq-iosched: Allow parent cgroup to preempt its child Currently we don't allow sync workload of one cgroup to preempt sync workload of any other cgroup. This is because we want to achieve service separation between cgroups. However in cases where cgroup preempting is ancestor of the current cgroup, there is no need of separation and idling introduces unnecessary overhead. This hurts for example the case when workload is isolated within a cgroup but journalling threads are in root cgroup. Simple way to demostrate the issue is using: dbench4 -c /usr/share/dbench4/client.txt -t 10 -D /mnt 1 on ext4 filesystem on plain SATA drive (mounted with barrier=0 to make difference more visible). When all processes are in the root cgroup, reported throughput is 153.132 MB/sec. When dbench process gets its own blkio cgroup, reported throughput drops to 26.1006 MB/sec. Fix the problem by making check in cfq_should_preempt() more benevolent and allow preemption by ancestor cgroup. This improves the throughput reported by dbench4 to 48.9106 MB/sec. Acked-by: Tejun Heo Signed-off-by: Jan Kara Signed-off-by: Jens Axboe Signed-off-by: anupritaisno1 --- block/cfq-iosched.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 9c8406103783b..c43e4012bad4f 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -614,6 +614,13 @@ static inline struct cfq_group *cfqg_parent(struct cfq_group *cfqg) return pblkg ? blkg_to_cfqg(pblkg) : NULL; } +static inline bool cfqg_is_descendant(struct cfq_group *cfqg, + struct cfq_group *ancestor) +{ + return cgroup_is_descendant(cfqg_to_blkg(cfqg)->blkcg->css.cgroup, + cfqg_to_blkg(ancestor)->blkcg->css.cgroup); +} + static inline void cfqg_get(struct cfq_group *cfqg) { return blkg_get(cfqg_to_blkg(cfqg)); @@ -754,6 +761,11 @@ static void cfqg_stats_xfer_dead(struct cfq_group *cfqg) #else /* CONFIG_CFQ_GROUP_IOSCHED */ static inline struct cfq_group *cfqg_parent(struct cfq_group *cfqg) { return NULL; } +static inline bool cfqg_is_descendant(struct cfq_group *cfqg, + struct cfq_group *ancestor) +{ + return true; +} static inline void cfqg_get(struct cfq_group *cfqg) { } static inline void cfqg_put(struct cfq_group *cfqg) { } @@ -3810,7 +3822,12 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, if (rq_is_sync(rq) && !cfq_cfqq_sync(cfqq) && !cfq_cfqq_must_dispatch(cfqq)) return true; - if (new_cfqq->cfqg != cfqq->cfqg) + /* + * Treat ancestors of current cgroup the same way as current cgroup. + * For anybody else we disallow preemption to guarantee service + * fairness among cgroups. + */ + if (!cfqg_is_descendant(cfqq->cfqg, new_cfqq->cfqg)) return false; if (cfq_slice_used(cfqq)) From 27b1d11395e9a2575a32579385e9358fd1563bbe Mon Sep 17 00:00:00 2001 From: Pranav Vashi Date: Sat, 15 Jul 2017 02:09:24 +0530 Subject: [PATCH 258/296] ASoC: wcdxxxx: queue work on power efficient wq Signed-off-by: Pranav Vashi --- sound/soc/codecs/wcd9320.c | 3 ++- sound/soc/codecs/wcd9330.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c index afbd3c2a24bc2..90d091acc39a7 100644 --- a/sound/soc/codecs/wcd9320.c +++ b/sound/soc/codecs/wcd9320.c @@ -3268,7 +3268,8 @@ static int taiko_codec_enable_dec(struct snd_soc_dapm_widget *w, CF_MIN_3DB_150HZ) && (tx_hpf_work[decimator - 1].tx_hpf_bypass != true)) { - schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork, + queue_delayed_work(system_power_efficient_wq, + &tx_hpf_work[decimator - 1].dwork, msecs_to_jiffies(300)); } /* apply the digital gain after the decimator is enabled*/ diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c index c28b0b0dbcff3..267818c0bd7aa 100644 --- a/sound/soc/codecs/wcd9330.c +++ b/sound/soc/codecs/wcd9330.c @@ -4145,7 +4145,8 @@ static int tomtom_codec_enable_dec(struct snd_soc_dapm_widget *w, CF_MIN_3DB_150HZ) && (tx_hpf_work[decimator - 1].tx_hpf_bypass != true)) { - schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork, + queue_delayed_work(system_power_efficient_wq, + &tx_hpf_work[decimator - 1].dwork, msecs_to_jiffies(300)); } /* apply the digital gain after the decimator is enabled*/ From fbf98e5b8a8716be1e24bd0f71d4ff52167a38e1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 24 Apr 2013 17:12:56 +0530 Subject: [PATCH 259/296] block: queue work on power efficient wq Block layer uses workqueues for multiple purposes. There is no real dependency of scheduling these on the cpu which scheduled them. On a idle system, it is observed that and idle cpu wakes up many times just to service this work. It would be better if we can schedule it on a cpu which the scheduler believes to be the most appropriate one. This patch replaces normal workqueues with power efficient versions. Cc: Jens Axboe Signed-off-by: Viresh Kumar Signed-off-by: Tejun Heo Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- block/blk-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/blk-core.c b/block/blk-core.c index 0214130e7499e..b050d08d99ea5 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -3334,7 +3334,8 @@ int __init blk_dev_init(void) /* used for unplugging and affects IO latency/throughput - HIGHPRI */ kblockd_workqueue = alloc_workqueue("kblockd", - WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + WQ_MEM_RECLAIM | WQ_HIGHPRI | + WQ_POWER_EFFICIENT, 0); if (!kblockd_workqueue) panic("Failed to create kblockd\n"); From 9e305c727588968cb38f220e5d4f484bff557cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Bj=C3=B8rling?= Date: Wed, 11 Jun 2014 23:43:54 +0200 Subject: [PATCH 260/296] block: remove WQ_POWER_EFFICIENT from kblockd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit blk-mq issues async requests through kblockd. To issue a work request on a specific CPU, kblockd_schedule_delayed_work_on is used. However, the specific CPU choice may not be honored, if the power_efficient option for workqueues is set. blk-mq requires that we have strict per-cpu scheduling, so it wont work properly if kblockd is marked POWER_EFFICIENT and power_efficient is set. Remove the kblockd WQ_POWER_EFFICIENT flag to prevent this behavior. This essentially reverts part of commit 695588f9454b, which added the WQ_POWER_EFFICIENT marker to kblockd. Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- block/blk-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index b050d08d99ea5..0214130e7499e 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -3334,8 +3334,7 @@ int __init blk_dev_init(void) /* used for unplugging and affects IO latency/throughput - HIGHPRI */ kblockd_workqueue = alloc_workqueue("kblockd", - WQ_MEM_RECLAIM | WQ_HIGHPRI | - WQ_POWER_EFFICIENT, 0); + WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); if (!kblockd_workqueue) panic("Failed to create kblockd\n"); From 991bd73287ca49c8feaf93eff81ca144f27f2b26 Mon Sep 17 00:00:00 2001 From: Shaibal Dutta Date: Fri, 31 Jan 2014 15:44:58 -0800 Subject: [PATCH 261/296] firmware: use power efficient workqueue for unloading and aborting fw load Allow the scheduler to select the most appropriate CPU for running the firmware load timeout routine and delayed routine for firmware unload. This extends idle residency times and conserves power. This functionality is enabled when CONFIG_WQ_POWER_EFFICIENT is selected. Cc: Ming Lei Cc: Greg Kroah-Hartman Signed-off-by: Shaibal Dutta [zoran.markovic@linaro.org: Rebased to latest kernel, added commit message. Fixed code alignment.] Signed-off-by: Zoran Markovic Signed-off-by: Greg Kroah-Hartman Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- drivers/base/firmware_class.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 05950f42056cd..a5455e3a5fede 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -1064,7 +1064,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, dev_set_uevent_suppress(f_dev, false); dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); if (timeout != MAX_SCHEDULE_TIMEOUT) - schedule_delayed_work(&fw_priv->timeout_work, timeout); + queue_delayed_work(system_power_efficient_wq, + &fw_priv->timeout_work, timeout); kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); } @@ -1769,8 +1770,8 @@ static void device_uncache_fw_images_work(struct work_struct *work) */ static void device_uncache_fw_images_delay(unsigned long delay) { - schedule_delayed_work(&fw_cache.work, - msecs_to_jiffies(delay)); + queue_delayed_work(system_power_efficient_wq, &fw_cache.work, + msecs_to_jiffies(delay)); } static int fw_pm_notify(struct notifier_block *notify_block, From a5d46aeb02ea95d1f33d03a1f40308c5ee2b14ae Mon Sep 17 00:00:00 2001 From: Shaibal Dutta Date: Thu, 30 Jan 2014 15:08:30 -0800 Subject: [PATCH 262/296] net: wireless: move regulatory timeout work to power efficient workqueue For better use of CPU idle time, allow the scheduler to select the CPU on which the timeout work of regulatory settings would be executed. This extends CPU idle residency time and saves power. This functionality is enabled when CONFIG_WQ_POWER_EFFICIENT is selected. Cc: "John W. Linville" Cc: "David S. Miller" Signed-off-by: Shaibal Dutta [zoran.markovic@linaro.org: Rebased to latest kernel. Added commit message.] Signed-off-by: Zoran Markovic Signed-off-by: Johannes Berg Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- net/wireless/reg.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 527f7a712ae6b..8f2efe40558a3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1574,8 +1574,8 @@ static void reg_process_hint(struct regulatory_request *reg_request, break; default: if (reg_initiator == NL80211_REGDOM_SET_BY_USER) - schedule_delayed_work(®_timeout, - msecs_to_jiffies(3142)); + queue_delayed_work(system_power_efficient_wq, + ®_timeout, msecs_to_jiffies(3142)); break; } } @@ -2177,7 +2177,8 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!request_wiphy && (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { - schedule_delayed_work(®_timeout, 0); + queue_delayed_work(system_power_efficient_wq, + ®_timeout, 0); return -ENODEV; } From 1467b14ca957001aa2212e60777bc500345aa85e Mon Sep 17 00:00:00 2001 From: Shaibal Dutta Date: Fri, 31 Jan 2014 11:53:06 -0800 Subject: [PATCH 263/296] rcu: Move SRCU grace period work to power efficient workqueue For better use of CPU idle time, allow the scheduler to select the CPU on which the SRCU grace period work would be scheduled. This improves idle residency time and conserves power. This functionality is enabled when CONFIG_WQ_POWER_EFFICIENT is selected. Cc: Lai Jiangshan Cc: "Paul E. McKenney" Cc: Dipankar Sarma Signed-off-by: Shaibal Dutta [zoran.markovic@linaro.org: Rebased to latest kernel version. Added commit message. Fixed code alignment.] Signed-off-by: Zoran Markovic Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- kernel/rcu/srcu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c index 01d5ccb8bfe3d..27b654950cc89 100644 --- a/kernel/rcu/srcu.c +++ b/kernel/rcu/srcu.c @@ -375,7 +375,7 @@ void call_srcu(struct srcu_struct *sp, struct rcu_head *head, rcu_batch_queue(&sp->batch_queue, head); if (!sp->running) { sp->running = true; - schedule_delayed_work(&sp->work, 0); + queue_delayed_work(system_power_efficient_wq, &sp->work, 0); } spin_unlock_irqrestore(&sp->queue_lock, flags); } @@ -631,7 +631,8 @@ static void srcu_reschedule(struct srcu_struct *sp) } if (pending) - schedule_delayed_work(&sp->work, SRCU_INTERVAL); + queue_delayed_work(system_power_efficient_wq, + &sp->work, SRCU_INTERVAL); } /* From ff32bbb5bdec395c715862c1d06f4b8d13bede86 Mon Sep 17 00:00:00 2001 From: viresh kumar Date: Wed, 22 Jan 2014 12:23:32 +0530 Subject: [PATCH 264/296] net/ipv4: queue work on power efficient wq Workqueue used in ipv4 layer have no real dependency of scheduling these on the cpu which scheduled them. On a idle system, it is observed that an idle cpu wakes up many times just to service this work. It would be better if we can schedule it on a cpu which the scheduler believes to be the most appropriate one. This patch replaces normal workqueues with power efficient versions. This doesn't change existing behavior of code unless CONFIG_WQ_POWER_EFFICIENT is enabled. Signed-off-by: Viresh Kumar Signed-off-by: David S. Miller Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- net/ipv4/devinet.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 802cb6ffe956f..a05484f02dfdf 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -473,7 +473,7 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, inet_hash_insert(dev_net(in_dev->dev), ifa); cancel_delayed_work(&check_lifetime_work); - schedule_delayed_work(&check_lifetime_work, 0); + queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); /* Send message first, then call notifier. Notifier will trigger FIB update, so that @@ -682,7 +682,8 @@ static void check_lifetime(struct work_struct *work) if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; - schedule_delayed_work(&check_lifetime_work, next_sched - now); + queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, + next_sched - now); } static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, @@ -838,7 +839,8 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) ifa = ifa_existing; set_ifa_lifetime(ifa, valid_lft, prefered_lft); cancel_delayed_work(&check_lifetime_work); - schedule_delayed_work(&check_lifetime_work, 0); + queue_delayed_work(system_power_efficient_wq, + &check_lifetime_work, 0); rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); } @@ -2303,7 +2305,7 @@ void __init devinet_init(void) register_gifconf(PF_INET, inet_gifconf); register_netdevice_notifier(&ip_netdev_notifier); - schedule_delayed_work(&check_lifetime_work, 0); + queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); rtnl_af_register(&inet_af_ops); From e487d24a8dd4a2e6d7c2baf4884b3b4ef4851cd2 Mon Sep 17 00:00:00 2001 From: Sergey Popovich Date: Fri, 6 Dec 2013 10:57:19 +0200 Subject: [PATCH 265/296] netfilter: xt_hashlimit: fix proc entry leak in netns destroy path In (32263dd1b netfilter: xt_hashlimit: fix namespace destroy path) the hashlimit_net_exit() function is always called right before hashlimit_mt_destroy() to release netns data. If you use xt_hashlimit with IPv4 and IPv6 together, this produces the following splat via netconsole in the netns destroy path: Pid: 9499, comm: kworker/u:0 Tainted: G WC O 3.2.0-5-netctl-amd64-core2 Call Trace: [] ? warn_slowpath_common+0x78/0x8c [] ? warn_slowpath_fmt+0x45/0x4a [] ? remove_proc_entry+0xd8/0x22e [] ? kfree+0x5b/0x6c [] ? hashlimit_net_exit+0x45/0x8d [xt_hashlimit] [] ? ops_exit_list+0x1c/0x44 [] ? cleanup_net+0xf1/0x180 [] ? should_resched+0x5/0x23 [] ? process_one_work+0x161/0x269 [] ? cwq_activate_delayed_work+0x3c/0x48 [] ? worker_thread+0xc2/0x145 [] ? manage_workers.isra.25+0x15b/0x15b [] ? kthread+0x76/0x7e [] ? kernel_thread_helper+0x4/0x10 [] ? kthread_worker_fn+0x139/0x139 [] ? gs_change+0x13/0x13 ---[ end trace d8c3cc0ad163ef79 ]--- ------------[ cut here ]------------ WARNING: at /usr/src/linux-3.2.52/debian/build/source_netctl/fs/proc/generic.c:849 remove_proc_entry+0x217/0x22e() Hardware name: remove_proc_entry: removing non-empty directory 'net/ip6t_hashlimit', leaking at least 'IN-REJECT' This is due to lack of removal net/ip6t_hashlimit/* entries in hashlimit_proc_net_exit(), since only IPv4 entries are deleted. Fix it by always removing the IPv4 and IPv6 entries and their parent directories in the netns destroy path. Signed-off-by: Sergey Popovich Signed-off-by: Pablo Neira Ayuso Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- net/netfilter/xt_hashlimit.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 9ff035c714032..a3910fc2122bc 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -325,21 +325,24 @@ static void htable_gc(unsigned long htlong) add_timer(&ht->timer); } -static void htable_destroy(struct xt_hashlimit_htable *hinfo) +static void htable_remove_proc_entry(struct xt_hashlimit_htable *hinfo) { struct hashlimit_net *hashlimit_net = hashlimit_pernet(hinfo->net); struct proc_dir_entry *parent; - del_timer_sync(&hinfo->timer); - if (hinfo->family == NFPROTO_IPV4) parent = hashlimit_net->ipt_hashlimit; else parent = hashlimit_net->ip6t_hashlimit; - if(parent != NULL) + if (parent != NULL) remove_proc_entry(hinfo->name, parent); +} +static void htable_destroy(struct xt_hashlimit_htable *hinfo) +{ + del_timer_sync(&hinfo->timer); + htable_remove_proc_entry(hinfo); htable_selective_cleanup(hinfo, select_all); kfree(hinfo->name); vfree(hinfo); @@ -883,21 +886,15 @@ static int __net_init hashlimit_proc_net_init(struct net *net) static void __net_exit hashlimit_proc_net_exit(struct net *net) { struct xt_hashlimit_htable *hinfo; - struct proc_dir_entry *pde; struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); - /* recent_net_exit() is called before recent_mt_destroy(). Make sure - * that the parent xt_recent proc entry is is empty before trying to - * remove it. + /* hashlimit_net_exit() is called before hashlimit_mt_destroy(). + * Make sure that the parent ipt_hashlimit and ip6t_hashlimit proc + * entries is empty before trying to remove it. */ mutex_lock(&hashlimit_mutex); - pde = hashlimit_net->ipt_hashlimit; - if (pde == NULL) - pde = hashlimit_net->ip6t_hashlimit; - hlist_for_each_entry(hinfo, &hashlimit_net->htables, node) - remove_proc_entry(hinfo->name, pde); - + htable_remove_proc_entry(hinfo); hashlimit_net->ipt_hashlimit = NULL; hashlimit_net->ip6t_hashlimit = NULL; mutex_unlock(&hashlimit_mutex); From dfab4e548250f71abd0891a749c9da903e37d962 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 24 Jul 2014 06:36:50 +0200 Subject: [PATCH 266/296] netfilter: xt_hashlimit: perform garbage collection from process context xt_hashlimit cannot be used with large hash tables, because garbage collector is run from a timer. If table is really big, its possible to hold cpu for more than 500 msec, which is unacceptable. Switch to a work queue, and use proper scheduling points to remove latencies spikes. Later, we also could switch to a smoother garbage collection done at lookup time, one bucket at a time... Signed-off-by: Eric Dumazet Cc: Florian Westphal Cc: Patrick McHardy Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Pranav Vashi Signed-off-by: anupritaisno1 --- net/netfilter/xt_hashlimit.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index a3910fc2122bc..47dc6836830a9 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -104,7 +104,7 @@ struct xt_hashlimit_htable { spinlock_t lock; /* lock for list_head */ u_int32_t rnd; /* random seed for hash */ unsigned int count; /* number entries in table */ - struct timer_list timer; /* timer for gc */ + struct delayed_work gc_work; /* seq_file stuff */ struct proc_dir_entry *pde; @@ -213,7 +213,7 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent) call_rcu_bh(&ent->rcu, dsthash_free_rcu); ht->count--; } -static void htable_gc(unsigned long htlong); +static void htable_gc(struct work_struct *work); static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) @@ -273,9 +273,9 @@ static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo, } hinfo->net = net; - setup_timer(&hinfo->timer, htable_gc, (unsigned long)hinfo); - hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); - add_timer(&hinfo->timer); + INIT_DEFERRABLE_WORK(&hinfo->gc_work, htable_gc); + queue_delayed_work(system_power_efficient_wq, &hinfo->gc_work, + msecs_to_jiffies(hinfo->cfg.gc_interval)); hlist_add_head(&hinfo->node, &hashlimit_net->htables); @@ -300,29 +300,30 @@ static void htable_selective_cleanup(struct xt_hashlimit_htable *ht, { unsigned int i; - /* lock hash table and iterate over it */ - spin_lock_bh(&ht->lock); for (i = 0; i < ht->cfg.size; i++) { struct dsthash_ent *dh; struct hlist_node *n; + + spin_lock_bh(&ht->lock); hlist_for_each_entry_safe(dh, n, &ht->hash[i], node) { if ((*select)(ht, dh)) dsthash_free(ht, dh); } + spin_unlock_bh(&ht->lock); + cond_resched(); } - spin_unlock_bh(&ht->lock); } -/* hash table garbage collector, run by timer */ -static void htable_gc(unsigned long htlong) +static void htable_gc(struct work_struct *work) { - struct xt_hashlimit_htable *ht = (struct xt_hashlimit_htable *)htlong; + struct xt_hashlimit_htable *ht; + + ht = container_of(work, struct xt_hashlimit_htable, gc_work.work); htable_selective_cleanup(ht, select_gc); - /* re-add the timer accordingly */ - ht->timer.expires = jiffies + msecs_to_jiffies(ht->cfg.gc_interval); - add_timer(&ht->timer); + queue_delayed_work(system_power_efficient_wq, + &ht->gc_work, msecs_to_jiffies(ht->cfg.gc_interval)); } static void htable_remove_proc_entry(struct xt_hashlimit_htable *hinfo) @@ -341,7 +342,7 @@ static void htable_remove_proc_entry(struct xt_hashlimit_htable *hinfo) static void htable_destroy(struct xt_hashlimit_htable *hinfo) { - del_timer_sync(&hinfo->timer); + cancel_delayed_work_sync(&hinfo->gc_work); htable_remove_proc_entry(hinfo); htable_selective_cleanup(hinfo, select_all); kfree(hinfo->name); From d4a6fb6177659d4bee35b6de4acfe8f56a32788f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 27 May 2015 11:09:39 +0930 Subject: [PATCH 267/296] kernel/workqueue.c: remove ifdefs over wq_power_efficient We can avoid an ifdef over wq_power_efficient's declaration by just using IS_ENABLED(). Cc: Rusty Russell Cc: Jani Nikula Cc: Andrew Morton Cc: Kees Cook Cc: Tejun Heo Cc: Ingo Molnar Cc: linux-kernel@vger.kernel.org Cc: cocci@systeme.lip6.fr Signed-off-by: Luis R. Rodriguez Signed-off-by: Rusty Russell Signed-off-by: anupritaisno1 --- kernel/workqueue.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 0ef60813a78b0..13f4cffaafd0f 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -274,12 +274,7 @@ static bool wq_disable_numa; module_param_named(disable_numa, wq_disable_numa, bool, 0444); /* see the comment above the definition of WQ_POWER_EFFICIENT */ -#ifdef CONFIG_WQ_POWER_EFFICIENT_DEFAULT -static bool wq_power_efficient = true; -#else -static bool wq_power_efficient; -#endif - +static bool wq_power_efficient = IS_ENABLED(CONFIG_WQ_POWER_EFFICIENT_DEFAULT); module_param_named(power_efficient, wq_power_efficient, bool, 0644); static bool wq_numa_enabled; /* unbound NUMA affinity enabled */ From bb812ea009cf91c0d0ed0b590cb2ece6a8314cc6 Mon Sep 17 00:00:00 2001 From: Zoran Markovic Date: Fri, 31 Jan 2014 09:48:00 -0800 Subject: [PATCH 268/296] power: move pm_qos update timeout handler to power-efficient workqueue To avoid waking up idle CPUs, allow the scheduler to select the best CPU to handle pm_qos update timeouts. This extends idle residency times and conserves power. This functionality is enabled when CONFIG_WQ_POWER_EFFICIENT is selected. Cc: Pavel Machek Cc: "Rafael J. Wysocki" Cc: Len Brown Signed-off-by: Shaibal Dutta [zoran.markovic@linaro.org: Rebased to latest kernel. Fixed code alignment. Added commit message.] Signed-off-by: Zoran Markovic Signed-off-by: mydongistiny Signed-off-by: anupritaisno1 --- kernel/power/qos.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 262442f548f34..1b375f95a8d18 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -574,7 +574,8 @@ void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value, pm_qos_array[req->pm_qos_class]->constraints, req, PM_QOS_UPDATE_REQ, new_value); - schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us)); + queue_delayed_work(system_power_efficient_wq, + &req->work, usecs_to_jiffies(timeout_us)); } /** From f45cf083729c43fbb3c24732e4ec1d03c7de899a Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sat, 16 Jul 2016 21:43:16 -0700 Subject: [PATCH 269/296] input: synaptics: s1302: Stop keypad when touchscreen is in use All credits to Sultanxda When the user's finger is on the touchscreen, don't process any keypad button presses, as an unintended button press will cause poor UX. Register an input handler to check when there are fingers on the touchscreen (via BTN_TOOL_FINGER) to disable/enable the keypad accordingly. Signed-off-by: anupritaisno1 --- .../touchscreen/synaptics_driver_s1302.c | 81 ++++++++++++++++++- .../touchscreen/synaptics_driver_s3320.c | 1 + 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/synaptics_driver_s1302.c b/drivers/input/touchscreen/synaptics_driver_s1302.c index afc8a4ef23804..67821c9170634 100644 --- a/drivers/input/touchscreen/synaptics_driver_s1302.c +++ b/drivers/input/touchscreen/synaptics_driver_s1302.c @@ -299,6 +299,8 @@ struct synaptics_ts_data { char fw_name[TP_FW_NAME_MAX_LEN]; char fw_id[12]; char manu_name[12]; + + bool stop_keypad; }; /* static int tc_hw_pwron(struct synaptics_ts_data *ts) @@ -753,10 +755,12 @@ static void synaptics_ts_report(struct synaptics_ts_data *ts ) if( inte & 0x10) { #ifdef VENDOR_EDIT //WayneChang, 2015/12/29, add flag to enable virtual key if(!virtual_key_enable){ - int_key(ts); + if (!ts->stop_keypad) + int_key(ts); } #else - int_key(ts); + if (!ts->stop_keypad) + int_key(ts); #endif } END: @@ -1512,6 +1516,74 @@ static int synaptics_parse_dts(struct device *dev, struct synaptics_ts_data *ts) } return rc; } + +static void synaptics_input_event(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + struct synaptics_ts_data *ts = tc_g; + + if (code != BTN_TOOL_FINGER) + return; + + /* Disable capacitive keys when user's finger is on touchscreen */ + ts->stop_keypad = value; +} + +static int synaptics_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int ret; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "s1302_handle"; + + ret = input_register_handle(handle); + if (ret) + goto err2; + + ret = input_open_device(handle); + if (ret) + goto err1; + + return 0; + +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return ret; +} + +static void synaptics_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id synaptics_input_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT, + .keybit = { [BIT_WORD(BTN_TOOL_FINGER)] = + BIT_MASK(BTN_TOOL_FINGER) }, + }, + { }, +}; + +static struct input_handler synaptics_input_handler = { + .event = synaptics_input_event, + .connect = synaptics_input_connect, + .disconnect = synaptics_input_disconnect, + .name = "syna_input_handler", + .id_table = synaptics_input_ids, +}; + static int synaptics_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { #ifdef CONFIG_SYNAPTIC_RED @@ -1630,6 +1702,11 @@ static int synaptics_ts_probe(struct i2c_client *client, const struct i2c_device register_remote_device_s1302(premote_data); } #endif + + ret = input_register_handler(&synaptics_input_handler); + if (ret) + TPD_ERR("%s: Failed to register input handler\n", __func__); + TPDTM_DMESG("synaptics_ts_probe s1302: normal end\n"); return 0; diff --git a/drivers/input/touchscreen/synaptics_driver_s3320.c b/drivers/input/touchscreen/synaptics_driver_s3320.c index b45b4a03b5b17..8293bf0ed40b1 100644 --- a/drivers/input/touchscreen/synaptics_driver_s3320.c +++ b/drivers/input/touchscreen/synaptics_driver_s3320.c @@ -2112,6 +2112,7 @@ static int synaptics_input_init(struct synaptics_ts_data *ts) #ifdef SUPPORT_GESTURE set_bit(KEY_F4 , ts->input_dev->keybit);//doulbe-tap resume + set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit); set_bit(KEY_WAKEUP , ts->input_dev->keybit); for (i = 250; i <= 248 + Down2UpSwip; i++) set_bit(i, ts->input_dev->keybit); From a7a84ab330571dd1d9fa079e9909bd71911c79f2 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sat, 16 Jul 2016 21:58:45 -0700 Subject: [PATCH 270/296] misc: fpc1020: Ignore home key presses when touchscreen is in use All credits to Sultanxda Ported to OnePlus2 by Lord Boeffla (aka andip71) When the user's finger is on the touchscreen, don't process any home key button presses, as an unintended button press will cause poor UX. Ignore home key presses when the Synaptics s1302 driver is ignoring keypad presses. Signed-off-by: Sultanxda Signed-off-by: flar2 Signed-off-by: anupritaisno1 --- drivers/input/misc/fpc1020_tee.c | 14 +++++++++++--- drivers/input/touchscreen/synaptics_driver_s1302.c | 7 +++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/fpc1020_tee.c b/drivers/input/misc/fpc1020_tee.c index 2bebf34337d3f..fd1f7e1b6050c 100755 --- a/drivers/input/misc/fpc1020_tee.c +++ b/drivers/input/misc/fpc1020_tee.c @@ -294,16 +294,24 @@ static DEVICE_ATTR(irq, S_IRUSR | S_IWUSR, irq_get, irq_ack); #ifdef VENDOR_EDIT //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system extern bool virtual_key_enable; #endif +extern bool s1302_is_keypad_stopped(void); static ssize_t report_home_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); - + + bool ignore_keypad; + + if (s1302_is_keypad_stopped() || virtual_key_enable) + ignore_keypad = true; + else + ignore_keypad = false; + if (!strncmp(buf, "down", strlen("down"))) { #ifdef VENDOR_EDIT //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system - if(!virtual_key_enable){ + if(!ignore_keypad){ input_report_key(fpc1020->input_dev, KEY_HOME, 1); input_sync(fpc1020->input_dev); @@ -313,7 +321,7 @@ static ssize_t report_home_set(struct device *dev, else if (!strncmp(buf, "up", strlen("up"))) { #ifdef VENDOR_EDIT //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system - if(!virtual_key_enable){ + if(!ignore_keypad){ input_report_key(fpc1020->input_dev, KEY_HOME, 0); input_sync(fpc1020->input_dev); diff --git a/drivers/input/touchscreen/synaptics_driver_s1302.c b/drivers/input/touchscreen/synaptics_driver_s1302.c index 67821c9170634..49326f1b75935 100644 --- a/drivers/input/touchscreen/synaptics_driver_s1302.c +++ b/drivers/input/touchscreen/synaptics_driver_s1302.c @@ -1517,6 +1517,13 @@ static int synaptics_parse_dts(struct device *dev, struct synaptics_ts_data *ts) return rc; } +bool s1302_is_keypad_stopped(void) +{ + struct synaptics_ts_data *ts = tc_g; + + return ts->stop_keypad; +} + static void synaptics_input_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { From 99ef85b50bcc32c1f2397e2d517a2814a11c639d Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Fri, 31 Mar 2017 20:47:09 +0800 Subject: [PATCH 271/296] misc: fpc1020: Fix s1302_is_keypad_stopped() logic for home key All credits to Sultanxda Ported to OnePlus2 by Wang Han (aka aviraxp) When the user's finger is on the touchscreen, don't process any home key button presses, as an unintended button press will cause poor UX. Ignore home key presses when the Synaptics s1302 driver is ignoring keypad presses. Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- drivers/input/misc/fpc1020_tee.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/fpc1020_tee.c b/drivers/input/misc/fpc1020_tee.c index fd1f7e1b6050c..8f7616380ce7f 100755 --- a/drivers/input/misc/fpc1020_tee.c +++ b/drivers/input/misc/fpc1020_tee.c @@ -321,7 +321,7 @@ static ssize_t report_home_set(struct device *dev, else if (!strncmp(buf, "up", strlen("up"))) { #ifdef VENDOR_EDIT //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system - if(!ignore_keypad){ + if(!virtual_key_enable){ input_report_key(fpc1020->input_dev, KEY_HOME, 0); input_sync(fpc1020->input_dev); From a19bff1832d7a31a329d97f265c88b9e144d0c27 Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Fri, 6 Jan 2017 12:38:01 -0800 Subject: [PATCH 272/296] input: synaptics: s1302: Make s1302_is_keypad_stopped() safe All credits to Sultanxda Add a trivial pointer check to prevent a null pointer dereference, since this function can be called globally. Signed-off-by: Sultanxda Signed-off-by: anupritaisno1 --- drivers/input/touchscreen/synaptics_driver_s1302.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/synaptics_driver_s1302.c b/drivers/input/touchscreen/synaptics_driver_s1302.c index 49326f1b75935..6fd1fdf68965e 100644 --- a/drivers/input/touchscreen/synaptics_driver_s1302.c +++ b/drivers/input/touchscreen/synaptics_driver_s1302.c @@ -1521,7 +1521,7 @@ bool s1302_is_keypad_stopped(void) { struct synaptics_ts_data *ts = tc_g; - return ts->stop_keypad; + return ts ? ts->stop_keypad : false; } static void synaptics_input_event(struct input_handle *handle, From 97488d9d5669cd39a436153ff332ea32f535708f Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Mon, 3 Sep 2018 19:11:39 -0700 Subject: [PATCH 273/296] uapi: Define __BITS_PER_LONG based on compiler target * We may compile 32-bit ARM code against these kernel headers in many situations, so provide a compiler-defined method of obtaining the width of long, rather than relying on a CFLAG define which may not be set in all cases. Change-Id: Iac5e48200d70f1258ab3caca1a8f1eb6e8f7f2d3 --- arch/arm64/include/uapi/asm/bitsperlong.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/include/uapi/asm/bitsperlong.h b/arch/arm64/include/uapi/asm/bitsperlong.h index fce9c2924fa34..71f93d50bb2d9 100644 --- a/arch/arm64/include/uapi/asm/bitsperlong.h +++ b/arch/arm64/include/uapi/asm/bitsperlong.h @@ -16,7 +16,11 @@ #ifndef __ASM_BITSPERLONG_H #define __ASM_BITSPERLONG_H +#ifdef __aarch64__ #define __BITS_PER_LONG 64 +#else +#define __BITS_PER_LONG 32 +#endif #include From 83cf2d846425a311c35c6324a6a4d042ba4b28cc Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Mon, 3 Sep 2018 23:37:48 -0700 Subject: [PATCH 274/296] uapi: Export sockaddr_storage for userspace Change-Id: I3851f40de8481ae32d43df3d96fe1fb9988233ce --- include/uapi/linux/socket.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/uapi/linux/socket.h b/include/uapi/linux/socket.h index 76ab0c68561ed..359c82ac38ff7 100644 --- a/include/uapi/linux/socket.h +++ b/include/uapi/linux/socket.h @@ -18,4 +18,11 @@ struct __kernel_sockaddr_storage { /* _SS_MAXSIZE value minus size of ss_family */ } __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */ + +#ifndef __KERNEL__ +/* This header is exposed as linux/socket.h, so it must provide definition + * for sockaddr_storage in place of include/linux/socket.h */ +#define sockaddr_storage __kernel_sockaddr_storage +#endif + #endif /* _UAPI_LINUX_SOCKET_H */ From b0f5a608e6bb5818889c746a91a566a7c317523c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 7 Jun 2013 01:20:27 -0400 Subject: [PATCH 275/296] [O_TMPFILE] it's still short a few helpers, but infrastructure should be OK now... Change-Id: I0adb8fe9c5029bad3ac52629003c3b78e9442936 Signed-off-by: Al Viro --- arch/alpha/include/uapi/asm/fcntl.h | 1 + arch/parisc/include/uapi/asm/fcntl.h | 1 + arch/sparc/include/uapi/asm/fcntl.h | 1 + fs/dcache.c | 16 ++++++++ fs/ext2/namei.c | 24 +++++++++++ fs/minix/namei.c | 13 ++++++ fs/namei.c | 60 ++++++++++++++++++++++++++++ fs/open.c | 14 ++++--- include/linux/dcache.h | 2 + include/linux/fs.h | 1 + include/uapi/asm-generic/fcntl.h | 4 ++ mm/shmem.c | 32 +++++++++++++++ 12 files changed, 164 insertions(+), 5 deletions(-) diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h index 6d9e805f18a77..dfdadb0b4befa 100644 --- a/arch/alpha/include/uapi/asm/fcntl.h +++ b/arch/alpha/include/uapi/asm/fcntl.h @@ -32,6 +32,7 @@ #define O_SYNC (__O_SYNC|O_DSYNC) #define O_PATH 040000000 +#define O_TMPFILE 0100000000 #define F_GETLK 7 #define F_SETLK 8 diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h index 0304b92ccfeaf..cc61c475f2779 100644 --- a/arch/parisc/include/uapi/asm/fcntl.h +++ b/arch/parisc/include/uapi/asm/fcntl.h @@ -20,6 +20,7 @@ #define O_INVISIBLE 004000000 /* invisible I/O, for DMAPI/XDSM */ #define O_PATH 020000000 +#define O_TMPFILE 040000000 #define F_GETLK64 8 #define F_SETLK64 9 diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h index d0b83f66f3564..d73e5e008b0de 100644 --- a/arch/sparc/include/uapi/asm/fcntl.h +++ b/arch/sparc/include/uapi/asm/fcntl.h @@ -35,6 +35,7 @@ #define O_SYNC (__O_SYNC|O_DSYNC) #define O_PATH 0x1000000 +#define O_TMPFILE 0x2000000 #define F_GETOWN 5 /* for sockets. */ #define F_SETOWN 6 /* for sockets. */ diff --git a/fs/dcache.c b/fs/dcache.c index aa42c7930c14d..624ea3e6089e9 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -3104,6 +3104,22 @@ void d_genocide(struct dentry *root) goto again; } +void d_tmpfile(struct dentry *dentry, struct inode *inode) +{ + inode_dec_link_count(inode); + BUG_ON(dentry->d_name.name != dentry->d_iname || + !hlist_unhashed(&dentry->d_u.d_alias) || + !d_unlinked(dentry)); + spin_lock(&dentry->d_parent->d_lock); + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + dentry->d_name.len = sprintf(dentry->d_iname, "#%llu", + (unsigned long long)inode->i_ino); + spin_unlock(&dentry->d_lock); + spin_unlock(&dentry->d_parent->d_lock); + d_instantiate(dentry, inode); +} +EXPORT_SYMBOL(d_tmpfile); + /** * find_inode_number - check for dentry with name * @dir: directory to check diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 73b0d9519836e..256dd5f4c1c4a 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -119,6 +119,29 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t mode return ext2_add_nondir(dentry, inode); } +static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct inode *inode = ext2_new_inode(dir, mode, NULL); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + inode->i_op = &ext2_file_inode_operations; + if (ext2_use_xip(inode->i_sb)) { + inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_fop = &ext2_xip_file_operations; + } else if (test_opt(inode->i_sb, NOBH)) { + inode->i_mapping->a_ops = &ext2_nobh_aops; + inode->i_fop = &ext2_file_operations; + } else { + inode->i_mapping->a_ops = &ext2_aops; + inode->i_fop = &ext2_file_operations; + } + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + unlock_new_inode(inode); + return 0; +} + static int ext2_mknod (struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct inode * inode; @@ -398,6 +421,7 @@ const struct inode_operations ext2_dir_inode_operations = { #endif .setattr = ext2_setattr, .get_acl = ext2_get_acl, + .tmpfile = ext2_tmpfile, }; const struct inode_operations ext2_special_inode_operations = { diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 0db73d9dd6684..cd950e2331b6c 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -54,6 +54,18 @@ static int minix_mknod(struct inode * dir, struct dentry *dentry, umode_t mode, return error; } +static int minix_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int error; + struct inode *inode = minix_new_inode(dir, mode, &error); + if (inode) { + minix_set_inode(inode, 0); + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + } + return error; +} + static int minix_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { @@ -254,4 +266,5 @@ const struct inode_operations minix_dir_inode_operations = { .mknod = minix_mknod, .rename = minix_rename, .getattr = minix_getattr, + .tmpfile = minix_tmpfile, }; diff --git a/fs/namei.c b/fs/namei.c index bff33267d7594..7623c0090ce7f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3003,6 +3003,61 @@ static int do_last(struct nameidata *nd, struct path *path, goto retry_lookup; } +static int do_tmpfile(int dfd, struct filename *pathname, + struct nameidata *nd, int flags, + const struct open_flags *op, + struct file *file, int *opened) +{ + static const struct qstr name = QSTR_INIT("/", 1); + struct dentry *dentry, *child; + struct inode *dir; + int error = path_lookupat(dfd, pathname->name, + flags | LOOKUP_DIRECTORY, nd); + if (unlikely(error)) + return error; + error = mnt_want_write(nd->path.mnt); + if (unlikely(error)) + goto out; + /* we want directory to be writable */ + error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC); + if (error) + goto out2; + dentry = nd->path.dentry; + dir = dentry->d_inode; + if (!dir->i_op->tmpfile) { + error = -EOPNOTSUPP; + goto out2; + } + child = d_alloc(dentry, &name); + if (unlikely(!child)) { + error = -ENOMEM; + goto out2; + } + nd->flags &= ~LOOKUP_DIRECTORY; + nd->flags |= op->intent; + dput(nd->path.dentry); + nd->path.dentry = child; + error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode); + if (error) + goto out2; + audit_inode(pathname, nd->path.dentry, 0); + error = may_open(&nd->path, op->acc_mode, op->open_flag); + if (error) + goto out2; + file->f_path.mnt = nd->path.mnt; + error = finish_open(file, nd->path.dentry, NULL, opened); + if (error) + goto out2; + error = open_check_o_direct(file); + if (error) + fput(file); +out2: + mnt_drop_write(nd->path.mnt); +out: + path_put(&nd->path); + return error; +} + static struct file *path_openat(int dfd, struct filename *pathname, struct nameidata *nd, const struct open_flags *op, int flags) { @@ -3018,6 +3073,11 @@ static struct file *path_openat(int dfd, struct filename *pathname, file->f_flags = op->open_flag; + if (unlikely(file->f_flags & O_TMPFILE)) { + error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened); + goto out; + } + error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base); if (unlikely(error)) goto out; diff --git a/fs/open.c b/fs/open.c index da01069ad4d43..bbaaaf3ed85cb 100644 --- a/fs/open.c +++ b/fs/open.c @@ -844,11 +844,15 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o if (flags & __O_SYNC) flags |= O_DSYNC; - /* - * If we have O_PATH in the open flag. Then we - * cannot have anything other than the below set of flags - */ - if (flags & O_PATH) { + if (flags & O_TMPFILE) { + if (!(flags & O_CREAT)) + return -EINVAL; + acc_mode = MAY_OPEN | ACC_MODE(flags); + } else if (flags & O_PATH) { + /* + * If we have O_PATH in the open flag. Then we + * cannot have anything other than the below set of flags + */ flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; acc_mode = 0; } else { diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 42e04e190f203..6e146d64ebe6e 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -249,6 +249,8 @@ extern struct dentry * d_make_root(struct inode *); /* - the ramfs-type tree */ extern void d_genocide(struct dentry *); +extern void d_tmpfile(struct dentry *, struct inode *); + extern struct dentry *d_find_alias(struct inode *); extern void d_prune_aliases(struct inode *); diff --git a/include/linux/fs.h b/include/linux/fs.h index e9c5f9e3c2a9d..a5c5605a0cba6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1617,6 +1617,7 @@ struct inode_operations { int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode, int *opened); + int (*tmpfile) (struct inode *, struct dentry *, umode_t); } ____cacheline_aligned; ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index a48937d4a5ea5..06632beaa6d51 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -84,6 +84,10 @@ #define O_PATH 010000000 #endif +#ifndef O_TMPFILE +#define O_TMPFILE 020000000 +#endif + #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif diff --git a/mm/shmem.c b/mm/shmem.c index 87e496fc4590a..ef0bba3c177c0 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2047,6 +2047,37 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) return error; } +static int +shmem_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct inode *inode; + int error = -ENOSPC; + + inode = shmem_get_inode(dir->i_sb, dir, mode, 0, VM_NORESERVE); + if (inode) { + error = security_inode_init_security(inode, dir, + NULL, + shmem_initxattrs, NULL); + if (error) { + if (error != -EOPNOTSUPP) { + iput(inode); + return error; + } + } +#ifdef CONFIG_TMPFS_POSIX_ACL + error = generic_acl_init(inode, dir); + if (error) { + iput(inode); + return error; + } +#else + error = 0; +#endif + d_tmpfile(dentry, inode); + } + return error; +} + static int shmem_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int error; @@ -2807,6 +2838,7 @@ static const struct inode_operations shmem_dir_inode_operations = { .rmdir = shmem_rmdir, .mknod = shmem_mknod, .rename = shmem_rename, + .tmpfile = shmem_tmpfile, #endif #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, From 2ed1e4222c3dd40e3f3edcd63a0f1f3f09b9b123 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Jun 2013 08:34:36 +0400 Subject: [PATCH 276/296] allow the temp files created by open() to be linked to O_TMPFILE | O_CREAT => linkat() with AT_SYMLINK_FOLLOW and /proc/self/fd/ as oldpath (i.e. flink()) will create a link O_TMPFILE | O_CREAT | O_EXCL => ENOENT on attempt to link those guys Change-Id: I5e28485680c3320cd0fccc0ba1bea8b963fca7fe Signed-off-by: Al Viro --- fs/inode.c | 4 +++- fs/namei.c | 16 ++++++++++++++-- include/linux/fs.h | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 18beae2118c96..52b3283e40a71 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -333,8 +333,10 @@ EXPORT_SYMBOL(set_nlink); */ void inc_nlink(struct inode *inode) { - if (WARN_ON(inode->i_nlink == 0)) + if (unlikely(inode->i_nlink == 0)) { + WARN_ON(!(inode->i_state & I_LINKABLE)); atomic_long_dec(&inode->i_sb->s_remove_count); + } inode->__i_nlink++; } diff --git a/fs/namei.c b/fs/namei.c index 7623c0090ce7f..eae94446c2160 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3049,8 +3049,14 @@ static int do_tmpfile(int dfd, struct filename *pathname, if (error) goto out2; error = open_check_o_direct(file); - if (error) + if (error) { fput(file); + } else if (!(op->open_flag & O_EXCL)) { + struct inode *inode = file_inode(file); + spin_lock(&inode->i_lock); + inode->i_state |= I_LINKABLE; + spin_unlock(&inode->i_lock); + } out2: mnt_drop_write(nd->path.mnt); out: @@ -3771,12 +3777,18 @@ int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir mutex_lock(&inode->i_mutex); /* Make sure we don't allow creating hardlink to an unlinked file */ - if (inode->i_nlink == 0) + if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE)) error = -ENOENT; else if (max_links && inode->i_nlink >= max_links) error = -EMLINK; else error = dir->i_op->link(old_dentry, dir, new_dentry); + + if (!error && (inode->i_state & I_LINKABLE)) { + spin_lock(&inode->i_lock); + inode->i_state &= ~I_LINKABLE; + spin_unlock(&inode->i_lock); + } mutex_unlock(&inode->i_mutex); if (!error) fsnotify_link(dir, inode, new_dentry); diff --git a/include/linux/fs.h b/include/linux/fs.h index a5c5605a0cba6..bfaf0bed5eb0c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1785,6 +1785,7 @@ struct super_operations { #define I_REFERENCED (1 << 8) #define __I_DIO_WAKEUP 9 #define I_DIO_WAKEUP (1 << I_DIO_WAKEUP) +#define I_LINKABLE (1 << 10) #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) From 2f859688344fc2cc54638b70bb664208cece9490 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Jun 2013 12:52:02 +0400 Subject: [PATCH 277/296] ext3 ->tmpfile() support In this case we do need a bit more than usual, due to orphan list handling. Change-Id: I355e9c97e04a03f89bb760009fecc160b25caeb7 Signed-off-by: Al Viro --- fs/ext3/namei.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index cea8ecf3e76e4..998ea111e537e 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1759,6 +1759,45 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry, return err; } +static int ext3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + handle_t *handle; + struct inode *inode; + int err, retries = 0; + + dquot_initialize(dir); + +retry: + handle = ext3_journal_start(dir, EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + + 4 + EXT3_XATTR_TRANS_BLOCKS); + + if (IS_ERR(handle)) + return PTR_ERR(handle); + + inode = ext3_new_inode (handle, dir, NULL, mode); + err = PTR_ERR(inode); + if (!IS_ERR(inode)) { + inode->i_op = &ext3_file_inode_operations; + inode->i_fop = &ext3_file_operations; + ext3_set_aops(inode); + err = ext3_orphan_add(handle, inode); + if (err) + goto err_drop_inode; + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + unlock_new_inode(inode); + } + ext3_journal_stop(handle); + if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) + goto retry; + return err; +err_drop_inode: + ext3_journal_stop(handle); + unlock_new_inode(inode); + iput(inode); + return err; +} + static int ext3_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode) { handle_t *handle; @@ -2300,7 +2339,7 @@ static int ext3_link (struct dentry * old_dentry, retry: handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT3_INDEX_EXTRA_TRANS_BLOCKS); + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 1); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2314,6 +2353,11 @@ static int ext3_link (struct dentry * old_dentry, err = ext3_add_entry(handle, dentry, inode); if (!err) { ext3_mark_inode_dirty(handle, inode); + /* this can happen only for tmpfile being + * linked the first time + */ + if (inode->i_nlink == 1) + ext3_orphan_del(handle, inode); d_instantiate(dentry, inode); } else { drop_nlink(inode); @@ -2516,6 +2560,7 @@ const struct inode_operations ext3_dir_inode_operations = { .mkdir = ext3_mkdir, .rmdir = ext3_rmdir, .mknod = ext3_mknod, + .tmpfile = ext3_tmpfile, .rename = ext3_rename, .setattr = ext3_setattr, #ifdef CONFIG_EXT3_FS_XATTR From e0552c58c30350fb4f20b6278e4824abf1c764c8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 29 Jun 2013 13:23:08 +0400 Subject: [PATCH 278/296] ext4: ->tmpfile() support very similar to ext3 counterpart... Change-Id: Ibb9de458c172ad50c4c202b971cb7243c8e43c82 Signed-off-by: Al Viro --- fs/ext4/namei.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 407bcf79aa31e..f36f3a1c0281c 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2297,6 +2297,45 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry, return err; } +static int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + handle_t *handle; + struct inode *inode; + int err, retries = 0; + + dquot_initialize(dir); + +retry: + inode = ext4_new_inode_start_handle(dir, mode, + NULL, 0, NULL, + EXT4_HT_DIR, + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + + 4 + EXT4_XATTR_TRANS_BLOCKS); + handle = ext4_journal_current_handle(); + err = PTR_ERR(inode); + if (!IS_ERR(inode)) { + inode->i_op = &ext4_file_inode_operations; + inode->i_fop = &ext4_file_operations; + ext4_set_aops(inode); + err = ext4_orphan_add(handle, inode); + if (err) + goto err_drop_inode; + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + unlock_new_inode(inode); + } + if (handle) + ext4_journal_stop(handle); + if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) + goto retry; + return err; +err_drop_inode: + ext4_journal_stop(handle); + unlock_new_inode(inode); + iput(inode); + return err; +} + struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode, struct ext4_dir_entry_2 *de, int blocksize, int csum_size, @@ -2904,7 +2943,7 @@ static int ext4_link(struct dentry *old_dentry, retry: handle = ext4_journal_start(dir, EXT4_HT_DIR, (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS)); + EXT4_INDEX_EXTRA_TRANS_BLOCKS) + 1); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2918,6 +2957,11 @@ static int ext4_link(struct dentry *old_dentry, err = ext4_add_entry(handle, dentry, inode); if (!err) { ext4_mark_inode_dirty(handle, inode); + /* this can happen only for tmpfile being + * linked the first time + */ + if (inode->i_nlink == 1) + ext4_orphan_del(handle, inode); d_instantiate(dentry, inode); } else { drop_nlink(inode); @@ -3170,6 +3214,7 @@ const struct inode_operations ext4_dir_inode_operations = { .mkdir = ext4_mkdir, .rmdir = ext4_rmdir, .mknod = ext4_mknod, + .tmpfile = ext4_tmpfile, .rename = ext4_rename, .setattr = ext4_setattr, .setxattr = generic_setxattr, From b44ed22d5d23a2f3e15b15c08435dbf72c12cd30 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 13 Jul 2013 13:26:37 +0400 Subject: [PATCH 279/296] Safer ABI for O_TMPFILE [suggested by Rasmus Villemoes] make O_DIRECTORY | O_RDWR part of O_TMPFILE; that will fail on old kernels in a lot more cases than what I came up with. And make sure O_CREAT doesn't get there... Change-Id: Iaa3c8b487d44515b539150bdb5d0b749b87d3ea2 Signed-off-by: Al Viro --- arch/alpha/include/uapi/asm/fcntl.h | 2 +- arch/parisc/include/uapi/asm/fcntl.h | 2 +- arch/sparc/include/uapi/asm/fcntl.h | 2 +- fs/namei.c | 2 +- fs/open.c | 4 ++-- include/uapi/asm-generic/fcntl.h | 8 ++++++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h index dfdadb0b4befa..09f49a6b87d18 100644 --- a/arch/alpha/include/uapi/asm/fcntl.h +++ b/arch/alpha/include/uapi/asm/fcntl.h @@ -32,7 +32,7 @@ #define O_SYNC (__O_SYNC|O_DSYNC) #define O_PATH 040000000 -#define O_TMPFILE 0100000000 +#define __O_TMPFILE 0100000000 #define F_GETLK 7 #define F_SETLK 8 diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h index cc61c475f2779..34a46cbc76ed5 100644 --- a/arch/parisc/include/uapi/asm/fcntl.h +++ b/arch/parisc/include/uapi/asm/fcntl.h @@ -20,7 +20,7 @@ #define O_INVISIBLE 004000000 /* invisible I/O, for DMAPI/XDSM */ #define O_PATH 020000000 -#define O_TMPFILE 040000000 +#define __O_TMPFILE 040000000 #define F_GETLK64 8 #define F_SETLK64 9 diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h index d73e5e008b0de..7e8ace5bf760a 100644 --- a/arch/sparc/include/uapi/asm/fcntl.h +++ b/arch/sparc/include/uapi/asm/fcntl.h @@ -35,7 +35,7 @@ #define O_SYNC (__O_SYNC|O_DSYNC) #define O_PATH 0x1000000 -#define O_TMPFILE 0x2000000 +#define __O_TMPFILE 0x2000000 #define F_GETOWN 5 /* for sockets. */ #define F_SETOWN 6 /* for sockets. */ diff --git a/fs/namei.c b/fs/namei.c index eae94446c2160..f45b1498d3d6e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3079,7 +3079,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, file->f_flags = op->open_flag; - if (unlikely(file->f_flags & O_TMPFILE)) { + if (unlikely(file->f_flags & __O_TMPFILE)) { error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened); goto out; } diff --git a/fs/open.c b/fs/open.c index bbaaaf3ed85cb..27586e7eeb019 100644 --- a/fs/open.c +++ b/fs/open.c @@ -844,8 +844,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o if (flags & __O_SYNC) flags |= O_DSYNC; - if (flags & O_TMPFILE) { - if (!(flags & O_CREAT)) + if (flags & __O_TMPFILE) { + if ((flags & O_TMPFILE_MASK) != O_TMPFILE) return -EINVAL; acc_mode = MAY_OPEN | ACC_MODE(flags); } else if (flags & O_PATH) { diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 06632beaa6d51..05ac354e124de 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -84,10 +84,14 @@ #define O_PATH 010000000 #endif -#ifndef O_TMPFILE -#define O_TMPFILE 020000000 +#ifndef __O_TMPFILE +#define __O_TMPFILE 020000000 #endif +/* a horrid kludge trying to make sure that this will fail on old kernels */ +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY | O_RDWR) +#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT | O_ACCMODE) + #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif From 4276b9060caf7d3e21f97349ff1477995568f1b8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 20 Jul 2013 03:11:32 +0400 Subject: [PATCH 280/296] allow O_TMPFILE to work with O_WRONLY Change-Id: I90171d1b53a4c35bfa76757ecfdfb6f95330d107 Signed-off-by: Al Viro --- fs/open.c | 2 ++ include/uapi/asm-generic/fcntl.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/open.c b/fs/open.c index 27586e7eeb019..1a51a1f526d50 100644 --- a/fs/open.c +++ b/fs/open.c @@ -848,6 +848,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o if ((flags & O_TMPFILE_MASK) != O_TMPFILE) return -EINVAL; acc_mode = MAY_OPEN | ACC_MODE(flags); + if (!(acc_mode & MAY_WRITE)) + return -EINVAL; } else if (flags & O_PATH) { /* * If we have O_PATH in the open flag. Then we diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 05ac354e124de..95e46c8e05f90 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -89,8 +89,8 @@ #endif /* a horrid kludge trying to make sure that this will fail on old kernels */ -#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY | O_RDWR) -#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT | O_ACCMODE) +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) +#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK From 8215365630458b228aecf17c2266f37a07fdfc86 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Sat, 20 Jul 2013 21:58:38 -0400 Subject: [PATCH 281/296] ext4: fix a BUG when opening a file with O_TMPFILE flag When we try to open a file with O_TMPFILE flag, we will trigger a bug. The root cause is that in ext4_orphan_add() we check ->i_nlink == 0 and this check always fails because we set ->i_nlink = 1 in inode_init_always(). We can use the following program to trigger it: int main(int argc, char *argv[]) { int fd; fd = open(argv[1], O_TMPFILE, 0666); if (fd < 0) { perror("open "); return -1; } close(fd); return 0; } The oops message looks like this: kernel BUG at fs/ext4/namei.c:2572! invalid opcode: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: dlci bridge stp hidp cmtp kernelcapi l2tp_ppp l2tp_netlink l2tp_core sctp libcrc32c rfcomm tun fuse nfnetli nk can_raw ipt_ULOG can_bcm x25 scsi_transport_iscsi ipx p8023 p8022 appletalk phonet psnap vmw_vsock_vmci_transport af_key vmw_vmci rose vsock atm can netrom ax25 af_rxrpc ir da pppoe pppox ppp_generic slhc bluetooth nfc rfkill rds caif_socket caif crc_ccitt af_802154 llc2 llc snd_hda_codec_realtek snd_hda_intel snd_hda_codec serio_raw snd_pcm pcsp kr edac_core snd_page_alloc snd_timer snd soundcore r8169 mii sr_mod cdrom pata_atiixp radeon backlight drm_kms_helper ttm CPU: 1 PID: 1812571 Comm: trinity-child2 Not tainted 3.11.0-rc1+ #12 Hardware name: Gigabyte Technology Co., Ltd. GA-MA78GM-S2H/GA-MA78GM-S2H, BIOS F12a 04/23/2010 task: ffff88007dfe69a0 ti: ffff88010f7b6000 task.ti: ffff88010f7b6000 RIP: 0010:[] [] ext4_orphan_add+0x299/0x2b0 RSP: 0018:ffff88010f7b7cf8 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ffff8800966d3020 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffff88007dfe70b8 RDI: 0000000000000001 RBP: ffff88010f7b7d40 R08: ffff880126a3c4e0 R09: ffff88010f7b7ca0 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8801271fd668 R13: ffff8800966d2f78 R14: ffff88011d7089f0 R15: ffff88007dfe69a0 FS: 00007f70441a3740(0000) GS:ffff88012a800000(0000) knlGS:00000000f77c96c0 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000002834000 CR3: 0000000107964000 CR4: 00000000000007e0 DR0: 0000000000780000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000600 Stack: 0000000000002000 00000020810b6dde 0000000000000000 ffff88011d46db00 ffff8800966d3020 ffff88011d7089f0 ffff88009c7f4c10 ffff88010f7b7f2c ffff88007dfe69a0 ffff88010f7b7da8 ffffffff8125cfac ffff880100000004 Call Trace: [] ext4_tmpfile+0x12c/0x180 [] path_openat+0x238/0x700 [] ? native_sched_clock+0x24/0x80 [] do_filp_open+0x47/0xa0 [] ? __alloc_fd+0xaf/0x200 [] do_sys_open+0x124/0x210 [] ? syscall_trace_enter+0x25/0x290 [] SyS_open+0x1e/0x20 [] tracesys+0xdd/0xe2 [] ? start_thread_common.constprop.6+0x1/0xa0 Code: 04 00 00 00 89 04 24 31 c0 e8 c4 77 04 00 e9 43 fe ff ff 66 25 00 d0 66 3d 00 80 0f 84 0e fe ff ff 83 7b 48 00 0f 84 04 fe ff ff <0f> 0b 49 8b 8c 24 50 07 00 00 e9 88 fe ff ff 0f 1f 84 00 00 00 Here we couldn't call clear_nlink() directly because in d_tmpfile() we will call inode_dec_link_count() to decrease ->i_nlink. So this commit tries to call d_tmpfile() before ext4_orphan_add() to fix this problem. Change-Id: Ie8a8009970d1e38c6863d94296f2738918da5429 Reported-by: Dave Jones Signed-off-by: Zheng Liu Tested-by: Darrick J. Wong Tested-by: Dave Jones Signed-off-by: "Theodore Ts'o" Acked-by: Al Viro --- fs/ext4/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index f36f3a1c0281c..c63b7cbdc750b 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2317,11 +2317,11 @@ static int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; ext4_set_aops(inode); + d_tmpfile(dentry, inode); err = ext4_orphan_add(handle, inode); if (err) goto err_drop_inode; mark_inode_dirty(inode); - d_tmpfile(dentry, inode); unlock_new_inode(inode); } if (handle) From 533214930372ed30324b42c691224746410fcd3c Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Sat, 20 Jul 2013 22:03:20 -0400 Subject: [PATCH 282/296] ext3: fix a BUG when opening a file with O_TMPFILE flag When we try to open a file with O_TMPFILE flag, we will trigger a bug. The root cause is that in ext4_orphan_add() we check ->i_nlink == 0 and this check always fails because we set ->i_nlink = 1 in inode_init_always(). We can use the following program to trigger it: int main(int argc, char *argv[]) { int fd; fd = open(argv[1], O_TMPFILE, 0666); if (fd < 0) { perror("open "); return -1; } close(fd); return 0; } The oops message looks like this: kernel: kernel BUG at fs/ext3/namei.c:1992! kernel: invalid opcode: 0000 [#1] SMP kernel: Modules linked in: ext4 jbd2 crc16 cpufreq_ondemand ipv6 dm_mirror dm_region_hash dm_log dm_mod parport_pc parport serio_raw sg dcdbas pcspkr i2c_i801 ehci_pci ehci_hcd button acpi_cpufreq mperf e1000e ptp pps_core ttm drm_kms_helper drm hwmon i2c_algo_bit i2c_core ext3 jbd sd_mod ahci libahci libata scsi_mod uhci_hcd kernel: CPU: 0 PID: 2882 Comm: tst_tmpfile Not tainted 3.11.0-rc1+ #4 kernel: Hardware name: Dell Inc. OptiPlex 780 /0V4W66, BIOS A05 08/11/2010 kernel: task: ffff880112d30050 ti: ffff8801124d4000 task.ti: ffff8801124d4000 kernel: RIP: 0010:[] [] ext3_orphan_add+0x6a/0x1eb [ext3] kernel: RSP: 0018:ffff8801124d5cc8 EFLAGS: 00010202 kernel: RAX: 0000000000000000 RBX: ffff880111510128 RCX: ffff8801114683a0 kernel: RDX: 0000000000000000 RSI: ffff880111510128 RDI: ffff88010fcf65a8 kernel: RBP: ffff8801124d5d18 R08: 0080000000000000 R09: ffffffffa00d3b7f kernel: R10: ffff8801114683a0 R11: ffff8801032a2558 R12: 0000000000000000 kernel: R13: ffff88010fcf6800 R14: ffff8801032a2558 R15: ffff8801115100d8 kernel: FS: 00007f5d172b5700(0000) GS:ffff880117c00000(0000) knlGS:0000000000000000 kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b kernel: CR2: 00007f5d16df15d0 CR3: 0000000110b1d000 CR4: 00000000000407f0 kernel: Stack: kernel: 000000000000000c ffff8801048a7dc8 ffff8801114685a8 ffffffffa00b80d7 kernel: ffff8801124d5e38 ffff8801032a2558 ffff88010ce24d68 0000000000000000 kernel: ffff88011146b300 ffff8801124d5d44 ffff8801124d5d78 ffffffffa00db7e1 kernel: Call Trace: kernel: [] ? journal_start+0x8c/0xbd [jbd] kernel: [] ext3_tmpfile+0xb2/0x13b [ext3] kernel: [] path_openat+0x11f/0x5e7 kernel: [] ? list_del+0x11/0x30 kernel: [] ? __dequeue_entity+0x33/0x38 kernel: [] do_filp_open+0x3f/0x8d kernel: [] ? __alloc_fd+0x50/0x102 kernel: [] do_sys_open+0x13b/0x1cd kernel: [] SyS_open+0x1e/0x20 kernel: [] system_call_fastpath+0x16/0x1b kernel: Code: 39 c7 0f 85 67 01 00 00 0f b7 03 25 00 f0 00 00 3d 00 40 00 00 74 18 3d 00 80 00 00 74 11 3d 00 a0 00 00 74 0a 83 7b 48 00 74 04 <0f> 0b eb fe 49 8b 85 50 03 00 00 4c 89 f6 48 c7 c7 c0 99 0e a0 kernel: RIP [] ext3_orphan_add+0x6a/0x1eb [ext3] kernel: RSP Here we couldn't call clear_nlink() directly because in d_tmpfile() we will call inode_dec_link_count() to decrease ->i_nlink. So this commit tries to call d_tmpfile() before ext4_orphan_add() to fix this problem. Change-Id: I6e953c0a1188d2099f9202e2f8ba8145fa3531b5 Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Cc: Jan Kara Cc: Al Viro --- fs/ext3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 998ea111e537e..1194b1f0f8396 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1780,11 +1780,11 @@ static int ext3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_op = &ext3_file_inode_operations; inode->i_fop = &ext3_file_operations; ext3_set_aops(inode); + d_tmpfile(dentry, inode); err = ext3_orphan_add(handle, inode); if (err) goto err_drop_inode; mark_inode_dirty(inode); - d_tmpfile(dentry, inode); unlock_new_inode(inode); } ext3_journal_stop(handle); From 80c26e505e9f38e3b1f688bf73c742e4092e1862 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Thu, 1 Aug 2013 21:07:52 -0700 Subject: [PATCH 283/296] fs: Fix file mode for O_TMPFILE O_TMPFILE, like O_CREAT, should respect the requested mode and should create regular files. This fixes two bugs: O_TMPFILE required privilege (because the mode ended up as 000) and it produced bogus inodes with no type. Change-Id: Ie4da9ede57e481c7edb113c5bc6329fefef41f4e Signed-off-by: Andy Lutomirski Signed-off-by: Al Viro --- fs/open.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/open.c b/fs/open.c index 1a51a1f526d50..013987c5499bd 100644 --- a/fs/open.c +++ b/fs/open.c @@ -827,7 +827,7 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o int lookup_flags = 0; int acc_mode; - if (flags & O_CREAT) + if (flags & (O_CREAT | __O_TMPFILE)) op->mode = (mode & S_IALLUGO) | S_IFREG; else op->mode = 0; From f7074cee9e848956673ced0cee15d99977e71473 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Thu, 25 Jul 2013 08:13:19 +0800 Subject: [PATCH 284/296] vfs: add missing check for __O_TMPFILE in fcntl_init() As comment in include/uapi/asm-generic/fcntl.h described, when introducing new O_* bits, we need to check its uniqueness in fcntl_init(). But __O_TMPFILE bit is missing. So fix it. Change-Id: I5c6ddf8ef05e96e4799bac823caa7dd60ce558dd Signed-off-by: Zheng Liu Signed-off-by: Al Viro --- fs/fcntl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/fcntl.c b/fs/fcntl.c index 6599222536eb2..65343c3741ff8 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -730,14 +730,14 @@ static int __init fcntl_init(void) * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY * is defined as O_NONBLOCK on some platforms and not on others. */ - BUILD_BUG_ON(19 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( + BUILD_BUG_ON(20 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | O_APPEND | /* O_NONBLOCK | */ __O_SYNC | O_DSYNC | FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | - __FMODE_EXEC | O_PATH + __FMODE_EXEC | O_PATH | __O_TMPFILE )); fasync_cache = kmem_cache_create("fasync_cache", From 5ff5d742e4397cce2812864c70550f5d272de7ed Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 10 Oct 2013 16:48:19 +0200 Subject: [PATCH 285/296] ext[34]: fix double put in tmpfile d_tmpfile() already swallowed the inode ref. Change-Id: I22411f145d675948cff55b5a8cc3c0cd3a0d484c Signed-off-by: Miklos Szeredi Cc: stable@vger.kernel.org Signed-off-by: Al Viro --- fs/ext3/namei.c | 5 ++--- fs/ext4/namei.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 1194b1f0f8396..f8cde46de9cd7 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1783,7 +1783,7 @@ static int ext3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) d_tmpfile(dentry, inode); err = ext3_orphan_add(handle, inode); if (err) - goto err_drop_inode; + goto err_unlock_inode; mark_inode_dirty(inode); unlock_new_inode(inode); } @@ -1791,10 +1791,9 @@ static int ext3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err; -err_drop_inode: +err_unlock_inode: ext3_journal_stop(handle); unlock_new_inode(inode); - iput(inode); return err; } diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index c63b7cbdc750b..5b8a1a5d75d3e 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2320,7 +2320,7 @@ static int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) d_tmpfile(dentry, inode); err = ext4_orphan_add(handle, inode); if (err) - goto err_drop_inode; + goto err_unlock_inode; mark_inode_dirty(inode); unlock_new_inode(inode); } @@ -2329,10 +2329,9 @@ static int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err; -err_drop_inode: +err_unlock_inode: ext4_journal_stop(handle); unlock_new_inode(inode); - iput(inode); return err; } From b7fb21cf46fc0f62649f75a80f17f58856802bfc Mon Sep 17 00:00:00 2001 From: Eric Rannaud Date: Thu, 30 Oct 2014 01:51:01 -0700 Subject: [PATCH 286/296] fs: allow open(dir, O_TMPFILE|..., 0) with mode 0 The man page for open(2) indicates that when O_CREAT is specified, the 'mode' argument applies only to future accesses to the file: Note that this mode applies only to future accesses of the newly created file; the open() call that creates a read-only file may well return a read/write file descriptor. The man page for open(2) implies that 'mode' is treated identically by O_CREAT and O_TMPFILE. O_TMPFILE, however, behaves differently: int fd = open("/tmp", O_TMPFILE | O_RDWR, 0); assert(fd == -1); assert(errno == EACCES); int fd = open("/tmp", O_TMPFILE | O_RDWR, 0600); assert(fd > 0); For O_CREAT, do_last() sets acc_mode to MAY_OPEN only: if (*opened & FILE_CREATED) { /* Don't check for write permission, don't truncate */ open_flag &= ~O_TRUNC; will_truncate = false; acc_mode = MAY_OPEN; path_to_nameidata(path, nd); goto finish_open_created; } But for O_TMPFILE, do_tmpfile() passes the full op->acc_mode to may_open(). This patch lines up the behavior of O_TMPFILE with O_CREAT. After the inode is created, may_open() is called with acc_mode = MAY_OPEN, in do_tmpfile(). A different, but related glibc bug revealed the discrepancy: https://sourceware.org/bugzilla/show_bug.cgi?id=17523 The glibc lazily loads the 'mode' argument of open() and openat() using va_arg() only if O_CREAT is present in 'flags' (to support both the 2 argument and the 3 argument forms of open; same idea for openat()). However, the glibc ignores the 'mode' argument if O_TMPFILE is in 'flags'. On x86_64, for open(), it magically works anyway, as 'mode' is in RDX when entering open(), and is still in RDX on SYSCALL, which is where the kernel looks for the 3rd argument of a syscall. But openat() is not quite so lucky: 'mode' is in RCX when entering the glibc wrapper for openat(), while the kernel looks for the 4th argument of a syscall in R10. Indeed, the syscall calling convention differs from the regular calling convention in this respect on x86_64. So the kernel sees mode = 0 when trying to use glibc openat() with O_TMPFILE, and fails with EACCES. Change-Id: Ib052bbc6fcc68d3060f91732a78ddbff6f71e0a6 Signed-off-by: Eric Rannaud Acked-by: Andy Lutomirski Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds --- fs/namei.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index f45b1498d3d6e..ef478c4af9ddd 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3041,7 +3041,8 @@ static int do_tmpfile(int dfd, struct filename *pathname, if (error) goto out2; audit_inode(pathname, nd->path.dentry, 0); - error = may_open(&nd->path, op->acc_mode, op->open_flag); + /* Don't check for other permissions, the inode was just created */ + error = may_open(&nd->path, MAY_OPEN, op->open_flag); if (error) goto out2; file->f_path.mnt = nd->path.mnt; From 323807e95fa3c1a8a81f222c0b660bd5b7af9495 Mon Sep 17 00:00:00 2001 From: dianlujitao Date: Mon, 14 May 2018 22:55:24 +0800 Subject: [PATCH 287/296] fs/proc: Override reboot boot mode with normal * AOSP framework/base commit dca36e4e loads persistent OEM specific USB functions with respect to the current boot mode if it's neither "normal" nor "unknown". In this case if the corresponding USB function is not set, it'll be set to "none". * However, while most bootloaders report boot mode as "normal" after reboot, OnePlus and OPPO bootloader reports "reboot". The system will treat it as OEM specific boot mode and try to load specific USB functions. As the functions are not set, sys.usb.config will be set to none. * To address the problem, hack cmdline to override "reboot" boot mode with "normal", so userspace won't confuse. Change-Id: Ie01b8177f05ca27832565b301bb3ddb79af0851e Signed-off-by: anupritaisno1 --- fs/proc/cmdline.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c index c275298f04cb6..b3135275710c7 100644 --- a/fs/proc/cmdline.c +++ b/fs/proc/cmdline.c @@ -43,6 +43,12 @@ static int __init proc_cmdline_init(void) strncpy(proc_cmdline, saved_command_line, COMMAND_LINE_SIZE); } + char *offset_addr; + + offset_addr = strstr(saved_command_line, "androidboot.mode=reboot"); + if (offset_addr != NULL) + strncpy(offset_addr + 17, "normal", 6); + proc_create("cmdline", 0, NULL, &cmdline_proc_fops); return 0; } From 132bef90e77730db5d2dcab5f617effd616fbe22 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Sun, 16 Sep 2018 23:05:19 +0100 Subject: [PATCH 288/296] dm-crypt: assume allow-discards is always true due to the commit by CAF in vold the allow_discards case never evaluates at all. instead of hacking vold assume that allow_discards is always true fixes: https://github.com/TeamWin/android_device_oneplus_oneplus2/issues/7#issuecomment-421841913 test: fstrim -v /data when using dm-req-crypt hold for review before mergimg to lineage-16.0 branch Signed-off-by: anupritaisno1 --- drivers/md/dm-crypt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index b1ff808312699..35c3a1b6d2a34 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1684,6 +1684,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) wake_up_process(cc->write_thread); ti->num_flush_bios = 1; + ti->num_discard_bios = 1; ti->discard_zeroes_data_unsupported = true; return 0; From 8a70be526f325845a5e75402daa3cfafc34dd3be Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 6 Jun 2013 09:12:33 -0400 Subject: [PATCH 289/296] do_last(): fix missing checks for LAST_BIND case /proc/self/cwd with O_CREAT should fail with EISDIR. /proc/self/exe, OTOH, should fail with ENOTDIR when opened with O_DIRECTORY. Change-Id: I01c85a6a3894c6854c604f192f221175edc19867 Signed-off-by: Al Viro Signed-off-by: anupritaisno1 --- fs/namei.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index ef478c4af9ddd..9347b94a6b6ef 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2768,28 +2768,10 @@ static int do_last(struct nameidata *nd, struct path *path, nd->flags &= ~LOOKUP_PARENT; nd->flags |= op->intent; - switch (nd->last_type) { - case LAST_DOTDOT: - case LAST_DOT: + if (nd->last_type != LAST_NORM) { error = handle_dots(nd, nd->last_type); if (error) return error; - /* fallthrough */ - case LAST_ROOT: - error = complete_walk(nd); - if (error) - return error; - audit_inode(name, nd->path.dentry, 0); - if (open_flag & O_CREAT) { - error = -EISDIR; - goto out; - } - goto finish_open; - case LAST_BIND: - error = complete_walk(nd); - if (error) - return error; - audit_inode(name, dir, 0); goto finish_open; } @@ -2920,19 +2902,19 @@ static int do_last(struct nameidata *nd, struct path *path, } nd->inode = inode; /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ +finish_open: error = complete_walk(nd); if (error) { path_put(&save_parent); return error; } + audit_inode(name, nd->path.dentry, 0); error = -EISDIR; if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode)) goto out; error = -ENOTDIR; if ((nd->flags & LOOKUP_DIRECTORY) && !can_lookup(nd->inode)) goto out; - audit_inode(name, nd->path.dentry, 0); -finish_open: if (!S_ISREG(nd->inode->i_mode)) will_truncate = false; From 25bf4db089423879363ef33a0c952842327e2a7a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Jun 2013 08:23:01 +0400 Subject: [PATCH 290/296] allow build_open_flags() to return an error note: using the mainline version instead of the version on the op2 los kernel repo because the EAS backport brought tnis code in-line with mainline Change-Id: I6e900fc3facf5a3febefe138fea7db493bc383d8 Signed-off-by: Al Viro Signed-off-by: anupritaisno1 --- fs/exec.c | 10 ++++++---- fs/internal.h | 5 +++-- fs/namei.c | 8 ++++---- fs/open.c | 49 ++++++++++++++++++++++++++++--------------------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 8978bfb765971..97afd3db3708e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -110,13 +110,14 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) static const struct open_flags uselib_flags = { .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, .acc_mode = MAY_READ | MAY_EXEC | MAY_OPEN, - .intent = LOOKUP_OPEN + .intent = LOOKUP_OPEN, + .lookup_flags = LOOKUP_FOLLOW, }; if (IS_ERR(tmp)) goto out; - file = do_filp_open(AT_FDCWD, tmp, &uselib_flags, LOOKUP_FOLLOW); + file = do_filp_open(AT_FDCWD, tmp, &uselib_flags); putname(tmp); error = PTR_ERR(file); if (IS_ERR(file)) @@ -776,10 +777,11 @@ struct file *open_exec(const char *name) static const struct open_flags open_exec_flags = { .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, .acc_mode = MAY_EXEC | MAY_OPEN, - .intent = LOOKUP_OPEN + .intent = LOOKUP_OPEN, + .lookup_flags = LOOKUP_FOLLOW, }; - file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags, LOOKUP_FOLLOW); + file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags); if (IS_ERR(file)) goto out; diff --git a/fs/internal.h b/fs/internal.h index db80be4c5b8d6..5b4193be8726c 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -95,11 +95,12 @@ struct open_flags { umode_t mode; int acc_mode; int intent; + int lookup_flags; }; extern struct file *do_filp_open(int dfd, struct filename *pathname, - const struct open_flags *op, int flags); + const struct open_flags *op); extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, - const char *, const struct open_flags *, int lookup_flags); + const char *, const struct open_flags *); extern long do_handle_open(int mountdirfd, struct file_handle __user *ufh, int open_flag); diff --git a/fs/namei.c b/fs/namei.c index 9347b94a6b6ef..9f7759b827f28 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3119,9 +3119,10 @@ static struct file *path_openat(int dfd, struct filename *pathname, } struct file *do_filp_open(int dfd, struct filename *pathname, - const struct open_flags *op, int flags) + const struct open_flags *op) { struct nameidata nd; + int flags = op->lookup_flags; struct file *filp; filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU); @@ -3133,17 +3134,16 @@ struct file *do_filp_open(int dfd, struct filename *pathname, } struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, - const char *name, const struct open_flags *op, int flags) + const char *name, const struct open_flags *op) { struct nameidata nd; struct file *file; struct filename filename = { .name = name }; + int flags = op->lookup_flags | LOOKUP_ROOT; nd.root.mnt = mnt; nd.root.dentry = dentry; - flags |= LOOKUP_ROOT; - if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN) return ERR_PTR(-ELOOP); diff --git a/fs/open.c b/fs/open.c index 013987c5499bd..48716c5e1f294 100644 --- a/fs/open.c +++ b/fs/open.c @@ -886,7 +886,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o lookup_flags |= LOOKUP_DIRECTORY; if (!(flags & O_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - return lookup_flags; + op->lookup_flags = lookup_flags; + return 0; } /** @@ -903,8 +904,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o struct file *file_open_name(struct filename *name, int flags, umode_t mode) { struct open_flags op; - int lookup = build_open_flags(flags, mode, &op); - return do_filp_open(AT_FDCWD, name, &op, lookup); + int err = build_open_flags(flags, mode, &op); + return err ? ERR_PTR(err) : do_filp_open(AT_FDCWD, name, &op); } /** @@ -929,37 +930,43 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, const char *filename, int flags) { struct open_flags op; - int lookup = build_open_flags(flags, 0, &op); + int err = build_open_flags(flags, 0, &op); + if (err) + return ERR_PTR(err); if (flags & O_CREAT) return ERR_PTR(-EINVAL); if (!filename && (flags & O_DIRECTORY)) if (!dentry->d_inode->i_op->lookup) return ERR_PTR(-ENOTDIR); - return do_file_open_root(dentry, mnt, filename, &op, lookup); + return do_file_open_root(dentry, mnt, filename, &op); } EXPORT_SYMBOL(file_open_root); long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; - int lookup = build_open_flags(flags, mode, &op); - struct filename *tmp = getname(filename); - int fd = PTR_ERR(tmp); - - if (!IS_ERR(tmp)) { - fd = get_unused_fd_flags(flags); - if (fd >= 0) { - struct file *f = do_filp_open(dfd, tmp, &op, lookup); - if (IS_ERR(f)) { - put_unused_fd(fd); - fd = PTR_ERR(f); - } else { - fsnotify_open(f); - fd_install(fd, f); - } + int fd = build_open_flags(flags, mode, &op); + struct filename *tmp; + + if (fd) + return fd; + + tmp = getname(filename); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + fd = get_unused_fd_flags(flags); + if (fd >= 0) { + struct file *f = do_filp_open(dfd, tmp, &op); + if (IS_ERR(f)) { + put_unused_fd(fd); + fd = PTR_ERR(f); + } else { + fsnotify_open(f); + fd_install(fd, f); } - putname(tmp); } + putname(tmp); return fd; } From 75dd2470cf0d0a4907daf884465316d3ed128612 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 22:53:15 -0400 Subject: [PATCH 291/296] path_openat(): fix double fput() [ Upstream commit f15133df088ecadd141ea1907f2c96df67c729f0 ] path_openat() jumps to the wrong place after do_tmpfile() - it has already done path_cleanup() (as part of path_lookupat() called by do_tmpfile()), so doing that again can lead to double fput(). Change-Id: Ia74c130ae5e379b512532c0feebea871b5f73668 Cc: stable@vger.kernel.org # v3.11+ Signed-off-by: Al Viro Signed-off-by: Sasha Levin Signed-off-by: anupritaisno1 --- fs/namei.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 9f7759b827f28..9f4dcb68212f2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3064,7 +3064,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, if (unlikely(file->f_flags & __O_TMPFILE)) { error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened); - goto out; + goto out2; } error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base); @@ -3102,6 +3102,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, path_put(&nd->root); if (base) fput(base); +out2: if (!(opened & FILE_OPENED)) { BUG_ON(!error); put_filp(file); From 451eba110b9533b2cb4e8de4d64dbdb120eaed85 Mon Sep 17 00:00:00 2001 From: Ranjith Kagathi Ananda Date: Fri, 4 Sep 2015 16:48:10 -0700 Subject: [PATCH 292/296] msm: ispif: Remove handling of SD_SHUTDOWN Remove handling of SD_SHUTDOWN to avoid multiple release. Bug: 31243641 Change-Id: I09db8adb766d2e7889443f779a716aaa2f6c09d1 Signed-off-by: Harsh Shah Signed-off-by: Ranjith Kagathi Ananda Signed-off-by: anupritaisno1 --- drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index d66700ebd02c2..b93bd99f9baed 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -1313,13 +1313,6 @@ static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd, case VIDIOC_MSM_ISPIF_CFG: return msm_ispif_cmd(sd, arg); case MSM_SD_SHUTDOWN: { - struct ispif_device *ispif = - (struct ispif_device *)v4l2_get_subdevdata(sd); - if (ispif && ispif->base) { - mutex_lock(&ispif->mutex); - msm_ispif_release(ispif); - mutex_unlock(&ispif->mutex); - } return 0; } default: From 5007e1e564bcbcb3bc8c54773c1ea07b81cb2b96 Mon Sep 17 00:00:00 2001 From: anupritaisno1 Date: Tue, 25 Sep 2018 19:06:09 +0100 Subject: [PATCH 293/296] Revert "KEYS: Don't permit request_key() to construct a new keyring" This reverts commit c65b86e1a9230304fe590e9e92478081b018be30. Signed-off-by: anupritaisno1 --- security/keys/request_key.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index e043521a3a1d1..c411f9bb156b2 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -458,9 +458,6 @@ static struct key *construct_key_and_link(struct key_type *type, kenter(""); - if (type == &key_type_keyring) - return ERR_PTR(-EPERM); - user = key_user_lookup(current_fsuid()); if (!user) return ERR_PTR(-ENOMEM); From 3378f82b9154ff2009272ed90745c4664f6245c2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 8 Dec 2017 15:13:27 +0000 Subject: [PATCH 294/296] KEYS: add missing permission check for request_key() destination 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") Change-Id: Ifed7bd6550a2562f6e307537fd7a17d8a7279f85 Cc: # v2.6.13+ Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: anupritaisno1 --- 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 c411f9bb156b2..52af4ffdead28 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -268,11 +268,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); @@ -281,6 +282,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) { @@ -295,8 +298,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: @@ -331,11 +336,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; } /* @@ -458,11 +481,15 @@ static struct key *construct_key_and_link(struct key_type *type, 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(type, description, dest_keyring, flags, user, &key); @@ -478,7 +505,7 @@ static struct key *construct_key_and_link(struct key_type *type, } else if (ret == -EINPROGRESS) { ret = 0; } else { - goto couldnt_alloc_key; + goto error_put_dest_keyring; } key_put(dest_keyring); @@ -488,8 +515,9 @@ static struct key *construct_key_and_link(struct key_type *type, 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); } From c27d368073dbcf92cf1f38fe8b6dff7156fbf930 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Fri, 6 Apr 2018 18:15:43 +0530 Subject: [PATCH 295/296] voice_svc: Avoid double free in voice_svc driver voice_svc_dev is allocated as a device managed resource and need not be freed since it is freed automatically. Remove the logic to free voice_svc_dev in probe failure and remove functions to avoid double free. CRs-Fixed: 2204285 Bug: 109741750 Change-Id: If4f9ca840b00448b987f5ce443f66b0923b01969 Signed-off-by: Aditya Bavanari Signed-off-by: anupritaisno1 --- drivers/soc/qcom/qdsp6v2/voice_svc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c index e05230255d6f0..ddd0802b7d0b2 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 @@ -773,7 +773,7 @@ static int voice_svc_probe(struct platform_device *pdev) if (ret) { pr_err("%s: Failed to alloc chrdev\n", __func__); ret = -ENODEV; - goto chrdev_err; + goto done; } voice_svc_dev->major = MAJOR(device_num); @@ -820,8 +820,6 @@ static int voice_svc_probe(struct platform_device *pdev) class_destroy(voice_svc_class); class_err: unregister_chrdev_region(0, MINOR_NUMBER); -chrdev_err: - kfree(voice_svc_dev); done: return ret; } @@ -835,7 +833,6 @@ static int voice_svc_remove(struct platform_device *pdev) device_destroy(voice_svc_class, device_num); class_destroy(voice_svc_class); unregister_chrdev_region(0, MINOR_NUMBER); - kfree(voice_svc_dev); return 0; } From 708ea8ba44ddd444988ce4ac159cb5d49c3fb3e6 Mon Sep 17 00:00:00 2001 From: Andrey Markovytch Date: Thu, 7 Jul 2016 09:12:59 +0300 Subject: [PATCH 296/296] md: dm-req-crypt: fixed error propagation when ICE is used When ICE is used errors during endio are not propagated properly to upper layers. Fixed. Change-Id: I5c428e4a02f7012f95e3a32486920e69b93ea3fc Signed-off-by: Andrey Markovytch Signed-off-by: anupritaisno1 --- drivers/md/dm-req-crypt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/md/dm-req-crypt.c b/drivers/md/dm-req-crypt.c index 41a9974609e53..07353e239ef4d 100644 --- a/drivers/md/dm-req-crypt.c +++ b/drivers/md/dm-req-crypt.c @@ -883,6 +883,7 @@ static int req_crypt_endio(struct dm_target *ti, struct request *clone, bvec = NULL; if (encryption_mode == DM_REQ_CRYPT_ENCRYPTION_MODE_TRANSPARENT) { mempool_free(req_io, req_io_pool); + err = error; goto submit_request; }